Compare commits

..

28 Commits

Author SHA1 Message Date
sweep-ai[bot]
23d36f2c89 Add 22deeee4712568fc942c828a18cfc607a0428e1e43d7c5a1f7fece8382cac76d_1445_flowchart.svg 2023-11-23 10:01:20 +00:00
sweep-ai[bot]
9bc84f241d Add cbd0785fc0f19918842878a6af340aa05bc834d0348d0ee15418e34bcc3a7b80_1445_flowchart.svg 2023-11-23 10:00:40 +00:00
fengyun.rui
a9153afc38 perf: control ws write buffer (#1451)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-22 09:31:37 +00:00
xuexihuang
7a13284b2e kafka work error ,alertmanager work error (#1455)
* Code adaptation k8s: service discovery and registration adaptation, configuration adaptation

* Initial submission of the help charts script for openim API

* change the help charts script

* change the help charts script

* change helm chart codes

* change dockerfiles script

* change chart script:add configmap mounts

* change chart script:change repository

* change chart script:msggateway add one service

* change config.yaml

* roll back some config values

* change chart script:change Ingress rule with a rewrite annotation

* add mysql charts scrible

* change chart script:add mysql.config.yaml

* add nfs provisioner charts

* change chart script:add nfs.config.yaml

* add ingress-nginx charts

* change chart script:add ingress-nginx.config.yaml

* add redis &mongodb charts

* add kafka&minio charts

* change chart script:change redis.values.yaml

* change chart script:add redis.config.yaml

* change chart script:change redis.config.yaml

* change chart script:change mongodb.value.yaml

* change chart script:change mongodb.value.yaml

* change chart script:add mongodb.config.yaml

* change chart script:change minio.values.yaml

* change chart script:add minio.config.yaml

* change chart script:change kafka.values.yaml

* change chart script:add kafka.config.yaml

* change chart script:change services.config.yaml

* bug fix:Delete websocket's Port restrictions

* bug fix:change port value

* change chart script:Submit a stable version script

* fix bug:Implement option interface

* fix bug:change K8sDR.Register

* change config.yaml

* change chats script:minio service add ingress

* change chats script:minio service add ingress

* change chats script:kafka.replicaCount=3& change minio.api ingress

* delete change chats script

* change config.yaml

* change openim.yaml

* merge go.sum

* Add monitoring function and struct for Prometheus on gin and GRPC

* Add GRPC and gin server monitoring logic

* Add GRPC and gin server monitoring logic2

* Add GRPC and gin server monitoring logic3

* Add GRPC and gin server monitoring logic4

* Add GRPC and gin server monitoring logic5

* Add GRPC and gin server monitoring logic6

* Add GRPC and gin server monitoring logic7

* delete:old monitoring code

* add for test

* fix bug:change packname

* fix bug:delete getPromPort funciton

* fix bug:delete getPromPort funciton

* fix bug:change logs

* fix bug:change registerName logic in GetGrpcCusMetrics function

* add getPrometheus url api

* fix:config path logic

* fix:prometheus enable function

* fix:prometheus enable function

* fix:transfer Multi process monitoring logic

* del:del not using manifest

* fix:openim-msgtransfer.sh

* fix:openim-msgtransfer.sh

* cicd: robot automated Change

* delete not using files

* add prometheus docker-compose for monitor

* fix prometheus.yaml

* fix environment.sh

* fix init-config.sh

* fix init-config.sh

* fix env_template.yaml

* fix docker-compose.yml

* fix docker-compose.yml

* add openim_admin_front service

* change openim-admin-front

* del not using files

* add node-exporter-dashaboard.yaml

* cicd: robot automated Change

* cicd: robot automated Change

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* del:delete not using files

* del:delete not using files

* change:change to personal email info

* fix:alertmanager.yml

* fix:fix docker-compose.yml

* del:not using files

---------

Co-authored-by: lin.huang <lin.huang@apulis.com>
Co-authored-by: Xinwei Xiong <3293172751@qq.com>
Co-authored-by: xuexihuang <xuexihuang@users.noreply.github.com>
Co-authored-by: cubxxw <cubxxw@users.noreply.github.com>
2023-11-21 09:09:31 +00:00
Xinwei Xiong
75375adf62 feat: deployment and design of management backend and monitoring (#1432)
Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
2023-11-18 04:36:30 +00:00
Xinwei Xiong
b17c6ec924 Update docker-compose.yml (#1425) 2023-11-16 10:47:04 +00:00
Xinwei Xiong
fb74453c18 Add Prometheus alerting functionality (#1424)
* Code adaptation k8s: service discovery and registration adaptation, configuration adaptation

* Initial submission of the help charts script for openim API

* change the help charts script

* change the help charts script

* change helm chart codes

* change dockerfiles script

* change chart script:add configmap mounts

* change chart script:change repository

* change chart script:msggateway add one service

* change config.yaml

* roll back some config values

* change chart script:change Ingress rule with a rewrite annotation

* add mysql charts scrible

* change chart script:add mysql.config.yaml

* add nfs provisioner charts

* change chart script:add nfs.config.yaml

* add ingress-nginx charts

* change chart script:add ingress-nginx.config.yaml

* add redis &mongodb charts

* add kafka&minio charts

* change chart script:change redis.values.yaml

* change chart script:add redis.config.yaml

* change chart script:change redis.config.yaml

* change chart script:change mongodb.value.yaml

* change chart script:change mongodb.value.yaml

* change chart script:add mongodb.config.yaml

* change chart script:change minio.values.yaml

* change chart script:add minio.config.yaml

* change chart script:change kafka.values.yaml

* change chart script:add kafka.config.yaml

* change chart script:change services.config.yaml

* bug fix:Delete websocket's Port restrictions

* bug fix:change port value

* change chart script:Submit a stable version script

* fix bug:Implement option interface

* fix bug:change K8sDR.Register

* change config.yaml

* change chats script:minio service add ingress

* change chats script:minio service add ingress

* change chats script:kafka.replicaCount=3& change minio.api ingress

* delete change chats script

* change config.yaml

* change openim.yaml

* merge go.sum

* Add monitoring function and struct for Prometheus on gin and GRPC

* Add GRPC and gin server monitoring logic

* Add GRPC and gin server monitoring logic2

* Add GRPC and gin server monitoring logic3

* Add GRPC and gin server monitoring logic4

* Add GRPC and gin server monitoring logic5

* Add GRPC and gin server monitoring logic6

* Add GRPC and gin server monitoring logic7

* delete:old monitoring code

* add for test

* fix bug:change packname

* fix bug:delete getPromPort funciton

* fix bug:delete getPromPort funciton

* fix bug:change logs

* fix bug:change registerName logic in GetGrpcCusMetrics function

* add getPrometheus url api

* fix:config path logic

* fix:prometheus enable function

* fix:prometheus enable function

* fix:transfer Multi process monitoring logic

* del:del not using manifest

* fix:openim-msgtransfer.sh

* fix:openim-msgtransfer.sh

* cicd: robot automated Change

* delete not using files

* add prometheus docker-compose for monitor

* fix prometheus.yaml

* fix environment.sh

* fix init-config.sh

* fix init-config.sh

* fix env_template.yaml

* fix docker-compose.yml

* fix docker-compose.yml

* add openim_admin_front service

* change openim-admin-front

* del not using files

* add node-exporter-dashaboard.yaml

* cicd: robot automated Change

* cicd: robot automated Change

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* feature: add alertmanager function

* del:delete not using files

* del:delete not using files

* change:change to personal email info

* feat: deployment and design of management backend and monitoring

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: deployment and design of management backend and monitoring

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: deployment and design of management backend and monitoring

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
Co-authored-by: lin.huang <lin.huang@apulis.com>
Co-authored-by: xuexihuang <1339326187@qq.com>
Co-authored-by: xuexihuang <xuexihuang@users.noreply.github.com>
Co-authored-by: cubxxw <cubxxw@users.noreply.github.com>
2023-11-16 10:02:30 +00:00
skiffer-git
82d238afbe Add files via upload 2023-11-16 14:36:50 +08:00
skiffer-git
2c9a2239d8 Delete docs/images/Wechat.jpg 2023-11-16 14:36:26 +08:00
skiffer-git
56fd78653c Add files via upload 2023-11-16 14:35:56 +08:00
skiffer-git
872dcae27a Update README-zh_CN.md 2023-11-16 14:34:35 +08:00
skiffer-git
6ba0d618e4 Add files via upload 2023-11-16 14:33:27 +08:00
fengyun.rui
a19f0e534f perf: redis batch delete msgs (#1395)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-15 17:14:26 +00:00
fengyun.rui
eeb16d4116 perf: broadcast msg to all gateway with concurrency (#1411)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-15 14:24:17 +00:00
Xinwei Xiong
ae048417ee "Add Prometheus Support with Documentation and Docker Compose Integration to OpenIM" (#1403)
* Code adaptation k8s: service discovery and registration adaptation, configuration adaptation

* Initial submission of the help charts script for openim API

* change the help charts script

* change the help charts script

* change helm chart codes

* change dockerfiles script

* change chart script:add configmap mounts

* change chart script:change repository

* change chart script:msggateway add one service

* change config.yaml

* roll back some config values

* change chart script:change Ingress rule with a rewrite annotation

* add mysql charts scrible

* change chart script:add mysql.config.yaml

* add nfs provisioner charts

* change chart script:add nfs.config.yaml

* add ingress-nginx charts

* change chart script:add ingress-nginx.config.yaml

* add redis &mongodb charts

* add kafka&minio charts

* change chart script:change redis.values.yaml

* change chart script:add redis.config.yaml

* change chart script:change redis.config.yaml

* change chart script:change mongodb.value.yaml

* change chart script:change mongodb.value.yaml

* change chart script:add mongodb.config.yaml

* change chart script:change minio.values.yaml

* change chart script:add minio.config.yaml

* change chart script:change kafka.values.yaml

* change chart script:add kafka.config.yaml

* change chart script:change services.config.yaml

* bug fix:Delete websocket's Port restrictions

* bug fix:change port value

* change chart script:Submit a stable version script

* fix bug:Implement option interface

* fix bug:change K8sDR.Register

* change config.yaml

* change chats script:minio service add ingress

* change chats script:minio service add ingress

* change chats script:kafka.replicaCount=3& change minio.api ingress

* delete change chats script

* change config.yaml

* change openim.yaml

* merge go.sum

* Add monitoring function and struct for Prometheus on gin and GRPC

* Add GRPC and gin server monitoring logic

* Add GRPC and gin server monitoring logic2

* Add GRPC and gin server monitoring logic3

* Add GRPC and gin server monitoring logic4

* Add GRPC and gin server monitoring logic5

* Add GRPC and gin server monitoring logic6

* Add GRPC and gin server monitoring logic7

* delete:old monitoring code

* add for test

* fix bug:change packname

* fix bug:delete getPromPort funciton

* fix bug:delete getPromPort funciton

* fix bug:change logs

* fix bug:change registerName logic in GetGrpcCusMetrics function

* add getPrometheus url api

* fix:config path logic

* fix:prometheus enable function

* fix:prometheus enable function

* fix:transfer Multi process monitoring logic

* del:del not using manifest

* fix:openim-msgtransfer.sh

* fix:openim-msgtransfer.sh

* cicd: robot automated Change

* delete not using files

* add prometheus docker-compose for monitor

* fix prometheus.yaml

* fix environment.sh

* fix init-config.sh

* fix init-config.sh

* fix env_template.yaml

* fix docker-compose.yml

* fix docker-compose.yml

* add openim_admin_front service

* change openim-admin-front

* del not using files

* add node-exporter-dashaboard.yaml

* fix: fix prom config file them

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* fix: fix prom config file them

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: Complete the source code module design of monitoring

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: Complete the source code module docs

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: Complete the source code module docs

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: add openim prometheus

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
Co-authored-by: lin.huang <lin.huang@apulis.com>
Co-authored-by: xuexihuang <1339326187@qq.com>
Co-authored-by: xuexihuang <xuexihuang@users.noreply.github.com>
2023-11-15 02:38:21 +00:00
fengyun.rui
7502b4ac0f refactor: lower the level of code nesting (#1396)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-14 06:21:44 +00:00
chao
05ab3fcd06 fix: the original quoted message is withdrawn and the quoted original message is displayed. (#1391)
* fix: GetUserReqApplicationList error when there is a disbanded group chat

* fix: error when querying some information about disbanded group

* fix: GetUserReqApplicationList dismissed group error

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* merge

* cicd: robot automated Change

* sdkws.MsgData

---------

Co-authored-by: withchao <withchao@users.noreply.github.com>
2023-11-14 02:47:56 +00:00
Gordon
7698368957 Bug/fix online status sync trigger (#1393)
* fix: sync close ws conn when kick old user avoid wrong trigger order about  online status.

* fix: reverse conversation notification id.
2023-11-14 02:01:08 +00:00
Xinwei Xiong
0d5fe4e6d6 Formatting adjustments, script removal, and helm template rendering (#1389)
* cicd: robot automated Change

* fix: add chat thmp

* fix: fix openim test file

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: add openim ctl system sctips remove

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: add openim cicd images

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: add openim cicd images

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: add openim config ete code

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>

* feat: fix openim pkg

---------

Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
Co-authored-by: cubxxw <cubxxw@users.noreply.github.com>
2023-11-13 13:27:17 +00:00
chao
2ac54e09a6 fix: the original quoted message is withdrawn and the quoted original message is displayed. (#1388)
* fix: GetUserReqApplicationList error when there is a disbanded group chat

* fix: error when querying some information about disbanded group

* fix: GetUserReqApplicationList dismissed group error

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* fix: the original message referenced by the pull message processing is withdrawn

* merge

* cicd: robot automated Change

---------

Co-authored-by: withchao <withchao@users.noreply.github.com>
2023-11-13 12:01:31 +00:00
fengyun.rui
7153eeb178 refactor: crontask cmd (#1331)
* refactor: cron task

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

* refactor: cron task

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

---------

Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-13 07:21:54 +00:00
fengyun.rui
fd42c6dced fix: reduce lock msg transfer (#1308)
* fix: reduce lock msg transfer

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

* fix: reduce lock msg transfer

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

---------

Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-13 05:17:50 +00:00
fengyun.rui
69eb24f702 perf: concurrent notify node on register (#1327)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-13 05:08:48 +00:00
fengyun.rui
65c1c412da refactor: gin prometheus for api (#1371)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-13 05:04:33 +00:00
fengyun.rui
2496a16a88 perf: add concurrency and pipeline for redis cache (#1338)
* perf: add concurrency and pipeline mode for redis cache

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

* perf: add concurrency and pipeline mode for redis cache

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

* perf: unit test for redis cache

Signed-off-by: rfyiamcool <rfyiamcool@163.com>

---------

Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-13 03:20:31 +00:00
fengyun.rui
d1af343b13 fix: add kafka compress type and producer ack params (#1310)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
2023-11-10 14:04:16 +00:00
fengyun.rui
a580c15f6a perf: improve gzip performance with sync.pool (#1321)
Signed-off-by: rfyiamcool <rfyiamcool@163.com>
Co-authored-by: Gordon <46924906+FGadvancer@users.noreply.github.com>
2023-11-10 14:02:02 +00:00
Xinwei Xiong
8e0cb6dc47 build: build openim image (#1381) 2023-11-10 13:14:21 +00:00
85 changed files with 5156 additions and 9459 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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
View File

@@ -29,8 +29,8 @@ PASSWORD=openIM123
MINIO_ENDPOINT=http://172.28.0.1:10005 MINIO_ENDPOINT=http://172.28.0.1:10005
# Base URL for the application programming interface (API). # Base URL for the application programming interface (API).
# Default: API_URL=http://172.0.0.1:10002 # Default: API_URL=http://172.28.0.1:10002
API_URL=http://172.0.0.1:10002 API_URL=http://172.28.0.1:10002
# Directory path for storing data files or related information. # Directory path for storing data files or related information.
# Default: DATA_DIR=./ # Default: DATA_DIR=./
@@ -98,7 +98,14 @@ PROMETHEUS_NETWORK_ADDRESS=172.28.0.11
# Address or hostname for the Grafana network. # Address or hostname for the Grafana network.
# Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12 # Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12
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 = # = Component Extension Configuration =
@@ -283,3 +290,19 @@ SERVER_BRANCH=main
# Port for the OpenIM admin API. # Port for the OpenIM admin API.
# Default: OPENIM_ADMIN_API_PORT=10009 # Default: OPENIM_ADMIN_API_PORT=10009
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

View File

@@ -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)

View File

@@ -52,6 +52,7 @@ jobs:
type=ref,event=branch type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=sha type=sha
@@ -87,6 +88,16 @@ jobs:
uses: docker/metadata-action@v5.0.0 uses: docker/metadata-action@v5.0.0
with: with:
images: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server 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 - name: Log in to AliYun Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -126,6 +137,7 @@ jobs:
type=ref,event=branch type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=sha type=sha

View File

@@ -15,11 +15,11 @@
name: Build OpenIM Web Docker image name: Build OpenIM Web Docker image
on: on:
schedule: # schedule:
- cron: '30 3 * * *' # - cron: '30 3 * * *'
push: push:
branches: branches:
- main # - main
- release-* - release-*
tags: tags:
- v* - v*

View File

@@ -97,4 +97,15 @@ jobs:
- name: Exec OpenIM System uninstall - name: Exec OpenIM System uninstall
run: | 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

View File

@@ -41,7 +41,7 @@ jobs:
# ./*.md all markdown files in the root directory # ./*.md all markdown files in the root directory
args: --verbose -E -i --no-progress --exclude-path './CHANGELOG' './**/*.md' args: --verbose -E -i --no-progress --exclude-path './CHANGELOG' './**/*.md'
env: env:
GITHUB_TOKEN: ${{secrets.GH_PAT}} GITHUB_TOKEN: ${{secrets.BOT_GITHUB_TOKEN}}
- name: Create Issue From File - name: Create Issue From File
if: env.lychee_exit_code != 0 if: env.lychee_exit_code != 0

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="447pt" height="165pt"
viewBox="0.00 0.00 447.00 164.74" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(36 128.74)">
<title>%3</title>
<polygon fill="dimgray" stroke="transparent" points="-36,36 -36,-128.74 411,-128.74 411,36 -36,36"/>
<text text-anchor="middle" x="187.5" y="-77.54" font-family="Helvetica,sans-Serif" font-size="14.00">Sweep&#39;s Plan &amp; Progress</text>
<!-- start -->
<g id="node1" class="node">
<title>start</title>
</g>
<!-- e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a -->
<g id="node3" class="node">
<title>e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a</title>
<ellipse fill="#ee0000" stroke="black" cx="184.5" cy="-26.87" rx="132.46" ry="26.74"/>
<text text-anchor="middle" x="184.5" y="-30.67" font-family="Helvetica,sans-Serif" font-size="14.00">✗ Modify</text>
<text text-anchor="middle" x="184.5" y="-15.67" font-family="Helvetica,sans-Serif" font-size="14.00">internal/rpc/user/user.go</text>
</g>
<!-- start&#45;&gt;e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a -->
<g id="edge1" class="edge">
<title>start&#45;&gt;e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a</title>
<path fill="none" stroke="black" d="M1.03,-26.87C4.1,-26.87 20.28,-26.87 41.85,-26.87"/>
<polygon fill="black" stroke="black" points="42.12,-30.37 52.12,-26.87 42.12,-23.37 42.12,-30.37"/>
<text text-anchor="middle" x="26.64" y="-33.67" font-family="Helvetica,sans-Serif" font-size="14.00">Start</text>
</g>
<!-- end -->
<g id="node2" class="node">
<title>end</title>
</g>
<!-- e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a&#45;&gt;end -->
<g id="edge2" class="edge">
<title>e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a&#45;&gt;end</title>
<path fill="none" stroke="black" d="M316.98,-26.87C336.34,-26.87 353.23,-26.87 363.61,-26.87"/>
<polygon fill="black" stroke="black" points="363.85,-30.37 373.85,-26.87 363.85,-23.37 363.85,-30.37"/>
<text text-anchor="middle" x="345.36" y="-33.67" font-family="Helvetica,sans-Serif" font-size="14.00">Finish</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -30,7 +30,7 @@
</p> </p>
## 🟢 扫描微信进群交流 ## 🟢 扫描微信进群交流
<img src="https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg" width="300"> <img src="./docs/images/Wechat.jpg" width="300">
## Ⓜ️ 关于 OpenIM ## Ⓜ️ 关于 OpenIM

View File

@@ -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 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 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) - [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 ## :busts_in_silhouette: Community

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="447pt" height="165pt"
viewBox="0.00 0.00 447.00 164.74" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(36 128.74)">
<title>%3</title>
<polygon fill="dimgray" stroke="transparent" points="-36,36 -36,-128.74 411,-128.74 411,36 -36,36"/>
<text text-anchor="middle" x="187.5" y="-77.54" font-family="Helvetica,sans-Serif" font-size="14.00">Sweep&#39;s Plan &amp; Progress</text>
<!-- start -->
<g id="node1" class="node">
<title>start</title>
</g>
<!-- e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a -->
<g id="node3" class="node">
<title>e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a</title>
<ellipse fill="yellow" stroke="black" cx="184.5" cy="-26.87" rx="132.46" ry="26.74"/>
<text text-anchor="middle" x="184.5" y="-30.67" font-family="Helvetica,sans-Serif" font-size="14.00">⋯ Modify</text>
<text text-anchor="middle" x="184.5" y="-15.67" font-family="Helvetica,sans-Serif" font-size="14.00">internal/rpc/user/user.go</text>
</g>
<!-- start&#45;&gt;e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a -->
<g id="edge1" class="edge">
<title>start&#45;&gt;e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a</title>
<path fill="none" stroke="black" d="M1.03,-26.87C4.1,-26.87 20.28,-26.87 41.85,-26.87"/>
<polygon fill="black" stroke="black" points="42.12,-30.37 52.12,-26.87 42.12,-23.37 42.12,-30.37"/>
<text text-anchor="middle" x="26.64" y="-33.67" font-family="Helvetica,sans-Serif" font-size="14.00">Start</text>
</g>
<!-- end -->
<g id="node2" class="node">
<title>end</title>
</g>
<!-- e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a&#45;&gt;end -->
<g id="edge2" class="edge">
<title>e9278104&#45;5ca8&#45;4ff3&#45;9348&#45;96db9869a52a&#45;&gt;end</title>
<path fill="none" stroke="black" d="M316.98,-26.87C336.34,-26.87 353.23,-26.87 363.61,-26.87"/>
<polygon fill="black" stroke="black" points="363.85,-30.37 373.85,-26.87 363.85,-23.37 363.85,-30.37"/>
<text text-anchor="middle" x="345.36" y="-33.67" font-family="Helvetica,sans-Serif" font-size="14.00">Finish</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
chat

Binary file not shown.

32
config/alertmanager.yml Normal file
View 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

View File

@@ -198,7 +198,7 @@ rpcRegisterName:
# Whether to output in json format # Whether to output in json format
# Whether to include stack trace in logs # Whether to include stack trace in logs
log: log:
storageLocation: ./logs/ storageLocation: ../logs/
rotationTime: 24 rotationTime: 24
remainRotationCount: 2 remainRotationCount: 2
remainLogLevel: 6 remainLogLevel: 6
@@ -382,7 +382,7 @@ callback:
# The number of Prometheus ports per service needs to correspond to rpcPort # 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 # The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus: prometheus:
enable: true enable: false
prometheusUrl: "https://openim.prometheus" prometheusUrl: "https://openim.prometheus"
apiPrometheusPort: [20100] apiPrometheusPort: [20100]
userPrometheusPort: [ 20110 ] userPrometheusPort: [ 20110 ]

16
config/email.tmpl Normal file
View 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 }}

View 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."

File diff suppressed because it is too large Load Diff

85
config/prometheus.yml Normal file
View 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'

View File

@@ -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: 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:** **Use Helmfile:**
```bash ```bash

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -96,7 +96,14 @@ verifyCode:
accessKeySecret: "" accessKeySecret: ""
signName: "" signName: ""
verificationCodeTemplateCode: "" verificationCodeTemplateCode: ""
mail: # 根据对应的发件邮箱更改 sendMail、senderAuthorizationCode、smtpAddr、smtpPort 即可
title: ""
senderMail: "" # 发送者
senderAuthorizationCode: "" # 授权码
smtpAddr: "smtp.qq.com" # smtp 服务器地址
smtpPort: 25 # smtp 服务器邮件发送端口
testDepartMentID: 001
imAPIURL: http://127.0.0.1:10002
###################### Proxy Header ###################### ###################### Proxy Header ######################
# 获取ip的header,没有配置直接获取远程地址 # 获取ip的header,没有配置直接获取远程地址

View File

@@ -94,12 +94,22 @@ OPENIM_CHAT_NETWORK_ADDRESS=${OPENIM_CHAT_NETWORK_ADDRESS}
# Address or hostname for the Prometheus network. # Address or hostname for the Prometheus network.
# Default: PROMETHEUS_NETWORK_ADDRESS=172.28.0.11 # Default: PROMETHEUS_NETWORK_ADDRESS=172.28.0.11
PROMETHEUS_NETWORK_ADDRESS=${PROMETHEUS_NETWORK_ADDRESS} PROMETHEUS_NETWORK_ADDRESS=${PROMETHEUS_NETWORK_ADDRESS}
# Address or hostname for the Grafana network. # Address or hostname for the Grafana network.
# Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12 # Default: GRAFANA_NETWORK_ADDRESS=172.28.0.12
GRAFANA_NETWORK_ADDRESS=${GRAFANA_NETWORK_ADDRESS} 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 = # = Component Extension Configuration =
# =============================================== # ===============================================
@@ -215,7 +225,7 @@ PROMETHEUS_PORT=${PROMETHEUS_PORT}
GRAFANA_ADDRESS=${GRAFANA_NETWORK_ADDRESS} GRAFANA_ADDRESS=${GRAFANA_NETWORK_ADDRESS}
# Port on which Grafana service is running. # Port on which Grafana service is running.
# Default: GRAFANA_PORT=3000 # Default: GRAFANA_PORT=13000
GRAFANA_PORT=${GRAFANA_PORT} GRAFANA_PORT=${GRAFANA_PORT}
# ====================================== # ======================================
@@ -283,3 +293,23 @@ SERVER_BRANCH=${SERVER_BRANCH}
# Port for the OpenIM admin API. # Port for the OpenIM admin API.
# Default: OPENIM_ADMIN_API_PORT=10009 # Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT} 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}

View 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'

View File

@@ -67,17 +67,17 @@ services:
ipv4_address: ${REDIS_NETWORK_ADDRESS} ipv4_address: ${REDIS_NETWORK_ADDRESS}
zookeeper: zookeeper:
image: bitnami/zookeeper:3.8 image: bitnami/zookeeper:3.8
container_name: zookeeper container_name: zookeeper
ports: ports:
- "${ZOOKEEPER_PORT}:2181" - "${ZOOKEEPER_PORT}:2181"
volumes: volumes:
- "/etc/localtime:/etc/localtime" - "/etc/localtime:/etc/localtime"
environment: environment:
- ALLOW_ANONYMOUS_LOGIN=yes - ALLOW_ANONYMOUS_LOGIN=yes
- TZ="Asia/Shanghai" - TZ="Asia/Shanghai"
restart: always restart: always
networks: networks:
server: server:
ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS} ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS}
@@ -142,3 +142,68 @@ services:
server: server:
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS} 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}

View File

@@ -150,7 +150,7 @@ For convenience, configuration through modifying environment variables is recomm
+ **Description**: API address. + **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. + **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" export API_URL="http://ip:10002"
``` ```
@@ -412,7 +412,7 @@ Configuration for Grafana, including its port and address.
| Parameter | Example Value | Description | | 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. | | GRAFANA_ADDRESS | "${DOCKER_BRIDGE_GATEWAY}" | Address for Grafana. |
### 2.16. <a name='RPCPortConfigurationVariables'></a>RPC Port Configuration Variables ### 2.16. <a name='RPCPortConfigurationVariables'></a>RPC Port Configuration Variables

View 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."
![image-20231114175117374](http://sm.nsddd.top/sm202311141751692.png)
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.
![image-20231114180351923](http://sm.nsddd.top/sm202311141803076.png)
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:**
![image-20231114194451673](http://sm.nsddd.top/sm202311141944953.png)
### 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
![image-20231115100811208](http://sm.nsddd.top/sm202311151008639.png)
#### 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:
![image-20231115101039837](http://sm.nsddd.top/sm202311151010116.png)
#### 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

4
go.mod
View File

@@ -44,6 +44,8 @@ require (
github.com/go-sql-driver/mysql v1.7.1 github.com/go-sql-driver/mysql v1.7.1
github.com/redis/go-redis/v9 v9.2.1 github.com/redis/go-redis/v9 v9.2.1
github.com/tencentyun/cos-go-sdk-v5 v0.7.45 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 gopkg.in/src-d/go-git.v4 v4.13.1
gotest.tools v2.2.0+incompatible gotest.tools v2.2.0+incompatible
) )
@@ -127,12 +129,10 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.13.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/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect

1
go.sum
View File

@@ -280,6 +280,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=

View File

@@ -15,11 +15,12 @@
package api package api
import ( import (
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"math/rand" "math/rand"
"net/http" "net/http"
"strconv" "strconv"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/protocol/third"

View File

@@ -167,7 +167,7 @@ func (c *Client) readMessage() {
func (c *Client) handleMessage(message []byte) error { func (c *Client) handleMessage(message []byte) error {
if c.IsCompress { if c.IsCompress {
var err error var err error
message, err = c.longConnServer.DeCompress(message) message, err = c.longConnServer.DecompressWithPool(message)
if err != nil { if err != nil {
return utils.Wrap(err, "") return utils.Wrap(err, "")
} }
@@ -317,7 +317,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error {
_ = c.conn.SetWriteDeadline(writeWait) _ = c.conn.SetWriteDeadline(writeWait)
if c.IsCompress { if c.IsCompress {
resultBuf, compressErr := c.longConnServer.Compress(encodedBuf) resultBuf, compressErr := c.longConnServer.CompressWithPool(encodedBuf)
if compressErr != nil { if compressErr != nil {
return utils.Wrap(compressErr, "") return utils.Wrap(compressErr, "")
} }

View File

@@ -17,14 +17,23 @@ package msggateway
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"errors"
"io" "io"
"sync"
"github.com/OpenIMSDK/tools/utils" "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 { type Compressor interface {
Compress(rawData []byte) ([]byte, error) Compress(rawData []byte) ([]byte, error)
CompressWithPool(rawData []byte) ([]byte, error)
DeCompress(compressedData []byte) ([]byte, error) DeCompress(compressedData []byte) ([]byte, error)
DecompressWithPool(compressedData []byte) ([]byte, error)
} }
type GzipCompressor struct { type GzipCompressor struct {
compressProtocol string compressProtocol string
@@ -46,6 +55,22 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
return gzipBuffer.Bytes(), nil 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) { func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
buff := bytes.NewBuffer(compressedData) buff := bytes.NewBuffer(compressedData)
reader, err := gzip.NewReader(buff) reader, err := gzip.NewReader(buff)
@@ -59,3 +84,23 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
_ = reader.Close() _ = reader.Close()
return compressedData, nil 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
}

View 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)
}
}

View File

@@ -17,22 +17,19 @@ package msggateway
import ( import (
"context" "context"
"github.com/OpenIMSDK/tools/mcontext"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/OpenIMSDK/tools/errs"
"google.golang.org/grpc" "google.golang.org/grpc"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/protocol/msggateway" "github.com/OpenIMSDK/protocol/msggateway"
"github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/discoveryregistry"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/log"
"github.com/OpenIMSDK/tools/mcontext"
"github.com/OpenIMSDK/tools/utils" "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/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "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 { if err != nil {
return err return err
} }
msgModel := cache.NewMsgCacheModel(rdb) msgModel := cache.NewMsgCacheModel(rdb)
s.LongConnServer.SetDiscoveryRegistry(disCov) s.LongConnServer.SetDiscoveryRegistry(disCov)
s.LongConnServer.SetCacheHandler(msgModel) s.LongConnServer.SetCacheHandler(msgModel)
@@ -97,22 +95,25 @@ func (s *Server) GetUsersOnlineStatus(
if !ok { if !ok {
continue continue
} }
temp := new(msggateway.GetUsersOnlineStatusResp_SuccessResult)
temp.UserID = userID uresp := new(msggateway.GetUsersOnlineStatusResp_SuccessResult)
uresp.UserID = userID
for _, client := range clients { for _, client := range clients {
if client != nil { if client == nil {
ps := new(msggateway.GetUsersOnlineStatusResp_SuccessDetail) continue
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)
} }
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 { if uresp.Status == constant.OnlineStatus {
resp.SuccessResult = append(resp.SuccessResult, temp) resp.SuccessResult = append(resp.SuccessResult, uresp)
} }
} }
return &resp, nil return &resp, nil
@@ -129,50 +130,55 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(
ctx context.Context, ctx context.Context,
req *msggateway.OnlineBatchPushOneMsgReq, req *msggateway.OnlineBatchPushOneMsgReq,
) (*msggateway.OnlineBatchPushOneMsgResp, error) { ) (*msggateway.OnlineBatchPushOneMsgResp, error) {
var singleUserResult []*msggateway.SingleMsgToUserResults
var singleUserResults []*msggateway.SingleMsgToUserResults
for _, v := range req.PushToUserIDs { for _, v := range req.PushToUserIDs {
var resp []*msggateway.SingleMsgToUserPlatform var resp []*msggateway.SingleMsgToUserPlatform
tempT := &msggateway.SingleMsgToUserResults{ results := &msggateway.SingleMsgToUserResults{
UserID: v, UserID: v,
} }
clients, ok := s.LongConnServer.GetUserAllCons(v) clients, ok := s.LongConnServer.GetUserAllCons(v)
if !ok { if !ok {
log.ZDebug(ctx, "push user not online", "userID", v) log.ZDebug(ctx, "push user not online", "userID", v)
tempT.Resp = resp results.Resp = resp
singleUserResult = append(singleUserResult, tempT) singleUserResults = append(singleUserResults, results)
continue continue
} }
log.ZDebug(ctx, "push user online", "clients", clients, "userID", v) log.ZDebug(ctx, "push user online", "clients", clients, "userID", v)
for _, client := range clients { for _, client := range clients {
if client != nil { if client == nil {
temp := &msggateway.SingleMsgToUserPlatform{ continue
RecvID: v, }
RecvPlatFormID: int32(client.PlatformID),
} userPlatform := &msggateway.SingleMsgToUserPlatform{
if !client.IsBackground || RecvID: v,
(client.IsBackground == true && client.PlatformID != constant.IOSPlatformID) { RecvPlatFormID: int32(client.PlatformID),
err := client.PushMessage(ctx, req.MsgData) }
if err != nil { if !client.IsBackground ||
temp.ResultCode = -2 (client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
resp = append(resp, temp) err := client.PushMessage(ctx, req.MsgData)
} else { if err != nil {
if utils.IsContainInt(client.PlatformID, s.pushTerminal) { userPlatform.ResultCode = -2
tempT.OnlinePush = true resp = append(resp, userPlatform)
resp = append(resp, temp)
}
}
} else { } else {
temp.ResultCode = -3 if utils.IsContainInt(client.PlatformID, s.pushTerminal) {
resp = append(resp, temp) results.OnlinePush = true
resp = append(resp, userPlatform)
}
} }
} else {
userPlatform.ResultCode = -3
resp = append(resp, userPlatform)
} }
} }
tempT.Resp = resp results.Resp = resp
singleUserResult = append(singleUserResult, tempT) singleUserResults = append(singleUserResults, results)
} }
return &msggateway.OnlineBatchPushOneMsgResp{ return &msggateway.OnlineBatchPushOneMsgResp{
SinglePushResult: singleUserResult, SinglePushResult: singleUserResults,
}, nil }, nil
} }
@@ -181,17 +187,21 @@ func (s *Server) KickUserOffline(
req *msggateway.KickUserOfflineReq, req *msggateway.KickUserOfflineReq,
) (*msggateway.KickUserOfflineResp, error) { ) (*msggateway.KickUserOfflineResp, error) {
for _, v := range req.KickUserIDList { for _, v := range req.KickUserIDList {
if clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID)); ok { clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID))
for _, client := range clients { if !ok {
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 {
log.ZInfo(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID) 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 return &msggateway.KickUserOfflineResp{}, nil
} }

View File

@@ -23,7 +23,7 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "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 { func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error {
fmt.Println( fmt.Println(
"start rpc/msg_gateway server, port: ", "start rpc/msg_gateway server, port: ",
@@ -37,7 +37,9 @@ func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error {
WithPort(wsPort), WithPort(wsPort),
WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)), WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)),
WithHandshakeTimeout(time.Duration(config.Config.LongConnSvr.WebsocketTimeout)*time.Second), 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 { if err != nil {
return err return err
} }

View File

@@ -50,10 +50,11 @@ type GWebSocket struct {
protocolType int protocolType int
conn *websocket.Conn conn *websocket.Conn
handshakeTimeout time.Duration handshakeTimeout time.Duration
writeBufferSize int
} }
func newGWebSocket(protocolType int, handshakeTimeout time.Duration) *GWebSocket { func newGWebSocket(protocolType int, handshakeTimeout time.Duration, wbs int) *GWebSocket {
return &GWebSocket{protocolType: protocolType, handshakeTimeout: handshakeTimeout} return &GWebSocket{protocolType: protocolType, handshakeTimeout: handshakeTimeout, writeBufferSize: wbs}
} }
func (d *GWebSocket) Close() error { func (d *GWebSocket) Close() error {
@@ -65,6 +66,10 @@ func (d *GWebSocket) GenerateLongConn(w http.ResponseWriter, r *http.Request) er
HandshakeTimeout: d.handshakeTimeout, HandshakeTimeout: d.handshakeTimeout,
CheckOrigin: func(r *http.Request) bool { return true }, 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) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return err return err

View File

@@ -23,26 +23,22 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/OpenIMSDK/protocol/msggateway" "github.com/go-playground/validator/v10"
"github.com/redis/go-redis/v9"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" "golang.org/x/sync/errgroup"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
"github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/protocol/msggateway"
"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/tools/discoveryregistry" "github.com/OpenIMSDK/tools/discoveryregistry"
"github.com/go-playground/validator/v10"
"github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/log"
"github.com/OpenIMSDK/tools/utils" "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 { type LongConnServer interface {
@@ -61,12 +57,6 @@ type LongConnServer interface {
MessageHandler MessageHandler
} }
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
type WsServer struct { type WsServer struct {
port int port int
wsMaxConnNum int64 wsMaxConnNum int64
@@ -78,7 +68,7 @@ type WsServer struct {
onlineUserNum atomic.Int64 onlineUserNum atomic.Int64
onlineUserConnNum atomic.Int64 onlineUserConnNum atomic.Int64
handshakeTimeout time.Duration handshakeTimeout time.Duration
hubServer *Server writeBufferSize int
validate *validator.Validate validate *validator.Validate
cache cache.MsgModel cache cache.MsgModel
userClient *rpcclient.UserRpcClient userClient *rpcclient.UserRpcClient
@@ -148,6 +138,7 @@ func NewWsServer(opts ...Option) (*WsServer, error) {
return &WsServer{ return &WsServer{
port: config.port, port: config.port,
wsMaxConnNum: config.maxConnNum, wsMaxConnNum: config.maxConnNum,
writeBufferSize: config.writeBufferSize,
handshakeTimeout: config.handshakeTimeout, handshakeTimeout: config.handshakeTimeout,
clientPool: sync.Pool{ clientPool: sync.Pool{
New: func() interface{} { New: func() interface{} {
@@ -183,27 +174,39 @@ func (ws *WsServer) Run() error {
return http.ListenAndServe(":"+utils.IntToString(ws.port), nil) // Start listening return http.ListenAndServe(":"+utils.IntToString(ws.port), nil) // Start listening
} }
var concurrentRequest = 3
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error { func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
conns, err := ws.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) conns, err := ws.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
if err != nil { if err != nil {
return err return err
} }
wg := errgroup.Group{}
wg.SetLimit(concurrentRequest)
// Online push user online message to other node // Online push user online message to other node
for _, v := range conns { for _, v := range conns {
v := v // safe closure var
if v.Target() == ws.disCov.GetSelfConnTarget() { if v.Target() == ws.disCov.GetSelfConnTarget() {
log.ZDebug(ctx, "Filter out this node", "node", v.Target()) log.ZDebug(ctx, "Filter out this node", "node", v.Target())
continue continue
} }
msgClient := msggateway.NewMsgGatewayClient(v)
_, err := msgClient.MultiTerminalLoginCheck(ctx, &msggateway.MultiTerminalLoginCheckReq{ wg.Go(func() error {
UserID: client.UserID, msgClient := msggateway.NewMsgGatewayClient(v)
PlatformID: int32(client.PlatformID), Token: client.token, _, 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 return nil
} }
@@ -289,70 +292,72 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
} }
fallthrough fallthrough
case constant.AllLoginButSameTermKick: case constant.AllLoginButSameTermKick:
if clientOK { if !clientOK {
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients) return
if isDeleteUser { }
ws.onlineUserNum.Add(-1)
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() m, err := ws.cache.GetTokensWithoutError(
if err != nil { newClient.ctx,
log.ZWarn(c.ctx, "KickOnlineMessage", err) newClient.UserID,
} newClient.PlatformID,
} )
m, err := ws.cache.GetTokensWithoutError( if err != nil && err != redis.Nil {
log.ZWarn(
newClient.ctx, newClient.ctx,
newClient.UserID, "get token from redis err",
newClient.PlatformID, err,
)
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",
"userID", "userID",
newClient.UserID, newClient.UserID,
"platformID", "platformID",
newClient.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 { for k := range m {
if k != newClient.ctx.GetToken() { if k != newClient.ctx.GetToken() {
m[k] = constant.KickedToken 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
} }
} }
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.onlineUserConnNum.Add(-1)
ws.SetUserOnlineStatus(client.ctx, client, constant.Offline) 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(), ws.onlineUserConnNum.Load(),
) )
} }
@@ -404,7 +409,7 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
httpError(connContext, errs.ErrConnArgsErr) httpError(connContext, errs.ErrConnArgsErr)
return return
} }
if err := authverify.WsVerifyToken(token, userID, platformID); err != nil { if err = authverify.WsVerifyToken(token, userID, platformID); err != nil {
httpError(connContext, err) httpError(connContext, err)
return return
} }
@@ -427,7 +432,8 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
httpError(connContext, errs.ErrTokenNotExist.Wrap()) httpError(connContext, errs.ErrTokenNotExist.Wrap())
return return
} }
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout)
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
err = wsLongConn.GenerateLongConn(w, r) err = wsLongConn.GenerateLongConn(w, r)
if err != nil { if err != nil {
httpError(connContext, err) httpError(connContext, err)

View File

@@ -27,6 +27,8 @@ type (
handshakeTimeout time.Duration handshakeTimeout time.Duration
// 允许消息最大长度 // 允许消息最大长度
messageMaxMsgLength int messageMaxMsgLength int
// websocket write buffer, default: 4096, 4kb.
writeBufferSize int
} }
) )
@@ -53,3 +55,9 @@ func WithMessageMaxMsgLength(length int) Option {
opt.messageMaxMsgLength = length opt.messageMaxMsgLength = length
} }
} }
func WithWriteBufferSize(size int) Option {
return func(opt *configs) {
opt.writeBufferSize = size
}
}

View File

@@ -21,14 +21,13 @@ import (
"net/http" "net/http"
"sync" "sync"
"github.com/OpenIMSDK/tools/mw"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "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/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"

View File

@@ -427,49 +427,62 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
break break
} }
} }
rwLock := new(sync.RWMutex)
log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset", log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset",
claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition()) 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() { go func() {
for { for {
select { select {
case <-t.C: case <-ticker.C:
if len(cMsg) > 0 { if len(messages) == 0 {
rwLock.Lock() continue
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))
} }
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() { for msg := range claim.Messages() {
rwLock.Lock() if len(msg.Value) == 0 {
if len(msg.Value) != 0 { continue
cMsg = append(cMsg, msg)
} }
rwLock.Lock()
messages = append(messages, msg)
rwLock.Unlock() rwLock.Unlock()
sess.MarkMessage(msg, "") sess.MarkMessage(msg, "")
} }
return nil return nil
} }

View File

@@ -2,6 +2,7 @@ package dummy
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
) )

View File

@@ -18,8 +18,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "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/constant"
"github.com/OpenIMSDK/protocol/conversation" "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/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "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/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "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 { if err != nil {
return nil, err return nil, err
} }
// Online push message
for _, v := range conns { var (
msgClient := msggateway.NewMsgGatewayClient(v) mu sync.Mutex
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}) wg = errgroup.Group{}
if err != nil { input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
continue maxWorkers = config.Config.Push.MaxConcurrentWorkers
} )
log.ZDebug(ctx, "push result", "reply", reply)
if reply != nil && reply.SinglePushResult != nil { if maxWorkers < 3 {
wsResults = append(wsResults, reply.SinglePushResult...) 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 return wsResults, nil
} }

View File

@@ -17,13 +17,18 @@ package tools
import ( import (
"context" "context"
"fmt" "fmt"
"sync" "os"
"os/signal"
"syscall"
"time"
"github.com/redis/go-redis/v9"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
"github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
) )
func StartTask() error { func StartTask() error {
@@ -32,23 +37,75 @@ func StartTask() error {
if err != nil { if err != nil {
return err return err
} }
msgTool.ConvertTools()
c := cron.New() msgTool.convertTools()
var wg sync.WaitGroup
wg.Add(1) 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) 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 { if err != nil {
log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err) log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err)
panic(err) panic(err)
} }
log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime) 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 { if err != nil {
log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err) log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err)
panic(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 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()
}
}
}

View 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()
}

View File

@@ -22,7 +22,7 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
) )
func (c *MsgTool) ConvertTools() { func (c *MsgTool) convertTools() {
ctx := mcontext.NewCtx("convert") ctx := mcontext.NewCtx("convert")
conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx) conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
if err != nil { if err != nil {

View File

@@ -22,37 +22,37 @@ import (
type SendMsg struct { type SendMsg struct {
// SendID uniquely identifies the sender. // SendID uniquely identifies the sender.
SendID string `json:"sendID" binding:"required"` SendID string `json:"sendID" binding:"required"`
// GroupID is the identifier for the group, required if SessionType is 2 or 3. // GroupID is the identifier for the group, required if SessionType is 2 or 3.
GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"` GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"`
// SenderNickname is the nickname of the sender. // SenderNickname is the nickname of the sender.
SenderNickname string `json:"senderNickname"` SenderNickname string `json:"senderNickname"`
// SenderFaceURL is the URL to the sender's avatar. // SenderFaceURL is the URL to the sender's avatar.
SenderFaceURL string `json:"senderFaceURL"` SenderFaceURL string `json:"senderFaceURL"`
// SenderPlatformID is an integer identifier for the sender's platform. // SenderPlatformID is an integer identifier for the sender's platform.
SenderPlatformID int32 `json:"senderPlatformID"` SenderPlatformID int32 `json:"senderPlatformID"`
// Content is the actual content of the message, required and excluded from Swagger documentation. // Content is the actual content of the message, required and excluded from Swagger documentation.
Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"`
// ContentType is an integer that represents the type of the content. // ContentType is an integer that represents the type of the content.
ContentType int32 `json:"contentType" binding:"required"` ContentType int32 `json:"contentType" binding:"required"`
// SessionType is an integer that represents the type of session for the message. // SessionType is an integer that represents the type of session for the message.
SessionType int32 `json:"sessionType" binding:"required"` SessionType int32 `json:"sessionType" binding:"required"`
// IsOnlineOnly specifies if the message is only sent when the receiver is online. // IsOnlineOnly specifies if the message is only sent when the receiver is online.
IsOnlineOnly bool `json:"isOnlineOnly"` IsOnlineOnly bool `json:"isOnlineOnly"`
// NotOfflinePush specifies if the message should not trigger offline push notifications. // NotOfflinePush specifies if the message should not trigger offline push notifications.
NotOfflinePush bool `json:"notOfflinePush"` NotOfflinePush bool `json:"notOfflinePush"`
// SendTime is a timestamp indicating when the message was sent. // SendTime is a timestamp indicating when the message was sent.
SendTime int64 `json:"sendTime"` SendTime int64 `json:"sendTime"`
// OfflinePushInfo contains information for offline push notifications. // OfflinePushInfo contains information for offline push notifications.
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"` OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
} }
@@ -67,10 +67,10 @@ type SendMsgReq struct {
// BatchSendMsgReq defines the structure for sending a message to multiple recipients. // BatchSendMsgReq defines the structure for sending a message to multiple recipients.
type BatchSendMsgReq struct { type BatchSendMsgReq struct {
SendMsg SendMsg
// IsSendAll indicates whether the message should be sent to all users. // IsSendAll indicates whether the message should be sent to all users.
IsSendAll bool `json:"isSendAll"` IsSendAll bool `json:"isSendAll"`
// RecvIDs is a slice of receiver identifiers to whom the message will be sent, required field. // RecvIDs is a slice of receiver identifiers to whom the message will be sent, required field.
RecvIDs []string `json:"recvIDs" binding:"required"` RecvIDs []string `json:"recvIDs" binding:"required"`
} }
@@ -79,7 +79,7 @@ type BatchSendMsgReq struct {
type BatchSendMsgResp struct { type BatchSendMsgResp struct {
// Results is a slice of SingleReturnResult, representing the outcome of each message sent. // Results is a slice of SingleReturnResult, representing the outcome of each message sent.
Results []*SingleReturnResult `json:"results"` Results []*SingleReturnResult `json:"results"`
// FailedIDs is a slice of user IDs for whom the message send failed. // FailedIDs is a slice of user IDs for whom the message send failed.
FailedIDs []string `json:"failedUserIDs"` FailedIDs []string `json:"failedUserIDs"`
} }
@@ -88,13 +88,13 @@ type BatchSendMsgResp struct {
type SingleReturnResult struct { type SingleReturnResult struct {
// ServerMsgID is the message identifier on the server-side. // ServerMsgID is the message identifier on the server-side.
ServerMsgID string `json:"serverMsgID"` ServerMsgID string `json:"serverMsgID"`
// ClientMsgID is the message identifier on the client-side. // ClientMsgID is the message identifier on the client-side.
ClientMsgID string `json:"clientMsgID"` ClientMsgID string `json:"clientMsgID"`
// SendTime is the timestamp of when the message was sent. // SendTime is the timestamp of when the message was sent.
SendTime int64 `json:"sendTime"` SendTime int64 `json:"sendTime"`
// RecvID uniquely identifies the receiver of the message. // RecvID uniquely identifies the receiver of the message.
RecvID string `json:"recvID"` RecvID string `json:"recvID"`
} }

View File

@@ -16,10 +16,12 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/constant"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/internal/msgtransfer" "github.com/openimsdk/open-im-server/v3/internal/msgtransfer"
) )

View File

@@ -78,17 +78,20 @@ type configStruct struct {
} `yaml:"mongo"` } `yaml:"mongo"`
Redis struct { Redis struct {
ClusterMode bool `yaml:"clusterMode"` ClusterMode bool `yaml:"clusterMode"`
Address []string `yaml:"address"` Address []string `yaml:"address"`
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"password"` Password string `yaml:"password"`
EnablePipeline bool `yaml:"enablePipeline"`
} `yaml:"redis"` } `yaml:"redis"`
Kafka struct { Kafka struct {
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"password"` Password string `yaml:"password"`
Addr []string `yaml:"addr"` ProducerAck string `yaml:"producerAck"`
TLS *struct { CompressType string `yaml:"compressType"`
Addr []string `yaml:"addr"`
TLS *struct {
CACrt string `yaml:"caCrt"` CACrt string `yaml:"caCrt"`
ClientCrt string `yaml:"clientCrt"` ClientCrt string `yaml:"clientCrt"`
ClientKey string `yaml:"clientKey"` ClientKey string `yaml:"clientKey"`
@@ -193,11 +196,13 @@ type configStruct struct {
WebsocketMaxConnNum int `yaml:"websocketMaxConnNum"` WebsocketMaxConnNum int `yaml:"websocketMaxConnNum"`
WebsocketMaxMsgLen int `yaml:"websocketMaxMsgLen"` WebsocketMaxMsgLen int `yaml:"websocketMaxMsgLen"`
WebsocketTimeout int `yaml:"websocketTimeout"` WebsocketTimeout int `yaml:"websocketTimeout"`
WebsocketWriteBufferSize int `yaml:"websocketWriteBufferSize"`
} `yaml:"longConnSvr"` } `yaml:"longConnSvr"`
Push struct { Push struct {
Enable string `yaml:"enable"` MaxConcurrentWorkers int `yaml:"maxConcurrentWorkers"`
GeTui struct { Enable string `yaml:"enable"`
GeTui struct {
PushUrl string `yaml:"pushUrl"` PushUrl string `yaml:"pushUrl"`
AppKey string `yaml:"appKey"` AppKey string `yaml:"appKey"`
Intent string `yaml:"intent"` Intent string `yaml:"intent"`
@@ -229,6 +234,7 @@ type configStruct struct {
ChatRecordsClearTime string `yaml:"chatRecordsClearTime"` ChatRecordsClearTime string `yaml:"chatRecordsClearTime"`
MsgDestructTime string `yaml:"msgDestructTime"` MsgDestructTime string `yaml:"msgDestructTime"`
Secret string `yaml:"secret"` Secret string `yaml:"secret"`
EnableCronLocker bool `yaml:"enableCronLocker"`
TokenPolicy struct { TokenPolicy struct {
Expire int64 `yaml:"expire"` Expire int64 `yaml:"expire"`
} `yaml:"tokenPolicy"` } `yaml:"tokenPolicy"`

View File

@@ -28,12 +28,21 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
) )
var (
// singleton pattern.
redisClient redis.UniversalClient
)
const ( const (
maxRetry = 10 // number of retries maxRetry = 10 // number of retries
) )
// NewRedis Initialize redis connection. // NewRedis Initialize redis connection.
func NewRedis() (redis.UniversalClient, error) { func NewRedis() (redis.UniversalClient, error) {
if redisClient != nil {
return redisClient, nil
}
if len(config.Config.Redis.Address) == 0 { if len(config.Config.Redis.Address) == 0 {
return nil, errors.New("redis address is empty") 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) return nil, fmt.Errorf("redis ping %w", err)
} }
redisClient = rdb
return rdb, err return rdb, err
} }

View File

@@ -20,9 +20,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/dtm-labs/rockscache" "golang.org/x/sync/errgroup"
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
@@ -62,6 +60,8 @@ const (
uidPidToken = "UID_PID_TOKEN_STATUS:" uidPidToken = "UID_PID_TOKEN_STATUS:"
) )
var concurrentLimit = 3
type SeqCache interface { type SeqCache interface {
SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error
GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]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 { type msgCache struct {
metaCache metaCache
rdb redis.UniversalClient rdb redis.UniversalClient
expireTime time.Duration
rcClient *rockscache.Client
msgDocDatabase unrelationtb.MsgDocModelInterface
} }
func (c *msgCache) getMaxSeqKey(conversationID string) string { 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 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 { 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 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 { 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) { 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 { for _, seq := range seqs {
res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result() results = append(results, pipe.Get(ctx, c.getMessageCacheKey(conversationID, seq)))
if err != nil { }
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", 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) failedSeqs = append(failedSeqs, seq)
continue continue
} }
msg := sdkws.MsgData{} 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) log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
failedSeqs = append(failedSeqs, seq) failedSeqs = append(failedSeqs, seq)
continue continue
} }
if msg.Status == constant.MsgDeleted { if msg.Status == constant.MsgDeleted {
failedSeqs = append(failedSeqs, seq) failedSeqs = append(failedSeqs, seq)
continue continue
} }
seqMsgs = append(seqMsgs, &msg) seqMsgs = append(seqMsgs, &msg)
} }
return return
//pipe := c.rdb.Pipeline() }
//for _, v := range seqs {
// // MESSAGE_CACHE:169.254.225.224_reliability1653387820_0_1 func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
// key := c.getMessageCacheKey(conversationID, v) type entry struct {
// if err := pipe.Get(ctx, key).Err(); err != nil && err != redis.Nil { err error
// return nil, nil, err msg *sdkws.MsgData
// } }
//}
//result, err := pipe.Exec(ctx) wg := errgroup.Group{}
//for i, v := range result { wg.SetLimit(concurrentLimit)
// cmd := v.(*redis.StringCmd)
// if cmd.Err() != nil { results := make([]entry, len(seqs)) // set slice len/cap to length of seqs.
// failedSeqs = append(failedSeqs, seqs[i]) for idx, seq := range seqs {
// } else { // closure safe var
// msg := sdkws.MsgData{} idx := idx
// err = msgprocessor.String2Pb(cmd.Val(), &msg) seq := seq
// if err == nil {
// if msg.Status != constant.MsgDeleted { wg.Go(func() error {
// seqMsgs = append(seqMsgs, &msg) res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
// continue if err != nil {
// } log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
// } else { results[idx] = entry{err: err}
// log.ZWarn(ctx, "UnmarshalString failed", err, "conversationID", conversationID, "seq", seqs[i], "msg", cmd.Val()) return nil
// } }
// failedSeqs = append(failedSeqs, seqs[i])
// } msg := sdkws.MsgData{}
//} if err = msgprocessor.String2Pb(res, &msg); err != nil {
//return seqMsgs, failedSeqs, err 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) { 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 { for _, msg := range msgs {
s, err := msgprocessor.Pb2String(msg) s, err := msgprocessor.Pb2String(msg)
if err != nil { if err != nil {
return 0, errs.Wrap(err) return 0, errs.Wrap(err, "pb.marshal")
} }
key := c.getMessageCacheKey(conversationID, msg.Seq) 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 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 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 { 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 { 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 { 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 errs.Wrap(err)
} }
} }
return nil 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 { func (c *msgCache) CleanUpOneConversationAllMsg(ctx context.Context, conversationID string) error {
@@ -584,14 +658,6 @@ func (c *msgCache) CleanUpOneConversationAllMsg(ctx context.Context, conversatio
} }
} }
return nil 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 { func (c *msgCache) DelMsgFromCache(ctx context.Context, userID string, seqs []int64) error {

387
pkg/common/db/cache/msg_test.go vendored Normal file
View 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
}
}

View File

@@ -16,22 +16,27 @@ package controller
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"time" "time"
"github.com/OpenIMSDK/protocol/constant"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/log"
"go.mongodb.org/mongo-driver/mongo"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" 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/db/unrelation"
"github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"go.mongodb.org/mongo-driver/mongo"
pbmsg "github.com/OpenIMSDK/protocol/msg" pbmsg "github.com/OpenIMSDK/protocol/msg"
"github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/protocol/sdkws"
@@ -397,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) { 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) { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) {
// log.ZDebug(ctx, "getMsgBySeqs", "docID", docID, "seqs", 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 { if err != nil {
return nil, err return nil, err
} }
@@ -408,13 +413,71 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat
return totalMsgs, nil return totalMsgs, nil
} }
func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, seqs []int64) (totalMsgs []*unrelationtb.MsgInfoModel, err error) { func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][]*unrelationtb.MsgInfoModel, userID, conversationID string, msg *unrelationtb.MsgInfoModel) {
msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs) if msg.IsRead {
for _, msg := range msgs { msg.Msg.IsRead = true
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), &quoteMsg); 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(&quoteMsg)
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 return msgs, err
} }
@@ -422,7 +485,7 @@ func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID strin
log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end) log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end)
for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) {
log.ZDebug(ctx, "getMsgBySeqsRange", "docID", docID, "seqs", seqs) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@@ -144,9 +144,9 @@ func Test_BatchInsertChat2DB(t *testing.T) {
} }
func GetDB() *commonMsgDatabase { 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.Timeout = 60
config.Config.Mongo.Database = "openIM" config.Config.Mongo.Database = "openIM_v3"
// config.Config.Mongo.Source = "admin" // config.Config.Mongo.Source = "admin"
config.Config.Mongo.Username = "root" config.Config.Mongo.Username = "root"
config.Config.Mongo.Password = "openIM123" config.Config.Mongo.Password = "openIM123"
@@ -232,37 +232,17 @@ func Test_FindBySeq(t *testing.T) {
// } // }
//} //}
//func Test_Delete1(t *testing.T) { func TestName(t *testing.T) {
// config.Config.Mongo.DBAddress = []string{"192.168.44.128:37017"} db := GetDB()
// config.Config.Mongo.DBTimeout = 60 var seqs []int64
// config.Config.Mongo.DBDatabase = "openIM" for i := int64(1); i <= 4; i++ {
// config.Config.Mongo.DBSource = "admin" seqs = append(seqs, i)
// config.Config.Mongo.DBUserName = "root" }
// config.Config.Mongo.DBPassword = "openIM123" msgs, err := db.getMsgBySeqsRange(context.Background(), "4931176757", "si_3866692501_4931176757", seqs, seqs[0], seqs[len(seqs)-1])
// config.Config.Mongo.DBMaxPoolSize = 100 if err != nil {
// config.Config.Mongo.DBRetainChatRecords = 3650 t.Fatal(err)
// config.Config.Mongo.ChatRecordsClearTime = "0 2 * * 3" }
//
// mongo, err := unrelation.NewMongo() t.Log(msgs)
// 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)
// }
//
//}

View File

@@ -175,7 +175,6 @@ func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHa
return nil, err return nil, err
} }
if md5Sum := md5.Sum([]byte(strings.Join(partHashs, partSeparator))); hex.EncodeToString(md5Sum[:]) != upload.Hash { 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") return nil, errors.New("md5 mismatching")
} }
if info, err := c.StatObject(ctx, c.HashPath(upload.Hash)); err == nil { if info, err := c.StatObject(ctx, c.HashPath(upload.Hash)); err == nil {

View File

@@ -3,7 +3,7 @@ package ginprometheus
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@@ -17,40 +17,42 @@ import (
var defaultMetricPath = "/metrics" var defaultMetricPath = "/metrics"
// counter, counter_vec, gauge, gauge_vec, // counter, counter_vec, gauge, gauge_vec,
// histogram, histogram_vec, summary, summary_vec // histogram, histogram_vec, summary, summary_vec.
var reqCnt = &Metric{ var (
ID: "reqCnt", reqCounter = &Metric{
Name: "requests_total", ID: "reqCnt",
Description: "How many HTTP requests processed, partitioned by status code and HTTP method.", Name: "requests_total",
Type: "counter_vec", Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
Args: []string{"code", "method", "handler", "host", "url"}} Type: "counter_vec",
Args: []string{"code", "method", "handler", "host", "url"}}
var reqDur = &Metric{ reqDuration = &Metric{
ID: "reqDur", ID: "reqDur",
Name: "request_duration_seconds", Name: "request_duration_seconds",
Description: "The HTTP request latencies in seconds.", Description: "The HTTP request latencies in seconds.",
Type: "histogram_vec", Type: "histogram_vec",
Args: []string{"code", "method", "url"}, Args: []string{"code", "method", "url"},
} }
var resSz = &Metric{ resSize = &Metric{
ID: "resSz", ID: "resSz",
Name: "response_size_bytes", Name: "response_size_bytes",
Description: "The HTTP response sizes in bytes.", Description: "The HTTP response sizes in bytes.",
Type: "summary"} Type: "summary"}
var reqSz = &Metric{ reqSize = &Metric{
ID: "reqSz", ID: "reqSz",
Name: "request_size_bytes", Name: "request_size_bytes",
Description: "The HTTP request sizes in bytes.", Description: "The HTTP request sizes in bytes.",
Type: "summary"} Type: "summary"}
var standardMetrics = []*Metric{ standardMetrics = []*Metric{
reqCnt, reqCounter,
reqDur, reqDuration,
resSz, resSize,
reqSz, reqSize,
} }
)
/* /*
RequestCounterURLLabelMappingFn is a function which can be supplied to the middleware to control 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 type RequestCounterURLLabelMappingFn func(c *gin.Context) string
// Metric is a definition for the name, description, type, ID, and // 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 { type Metric struct {
MetricCollector prometheus.Collector MetricCollector prometheus.Collector
ID string ID string
@@ -84,7 +86,7 @@ type Metric struct {
Args []string 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 { type Prometheus struct {
reqCnt *prometheus.CounterVec reqCnt *prometheus.CounterVec
reqDur *prometheus.HistogramVec reqDur *prometheus.HistogramVec
@@ -102,7 +104,7 @@ type Prometheus struct {
URLLabelFromContext string 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 { type PrometheusPushGateway struct {
// Push interval in seconds // Push interval in seconds
@@ -112,7 +114,7 @@ type PrometheusPushGateway struct {
// where JOBNAME can be any string of your choice // where JOBNAME can be any string of your choice
PushGatewayURL string 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 // if implemented using prometheus common/expfmt instead
MetricsURL string MetricsURL string
@@ -120,9 +122,11 @@ type PrometheusPushGateway struct {
Job string 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 { func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus {
subsystem = "app" if subsystem == "" {
subsystem = "app"
}
var metricsList []*Metric var metricsList []*Metric
@@ -131,16 +135,13 @@ func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus
} else if len(customMetricsList) == 1 { } else if len(customMetricsList) == 1 {
metricsList = customMetricsList[0] metricsList = customMetricsList[0]
} }
metricsList = append(metricsList, standardMetrics...)
for _, metric := range standardMetrics {
metricsList = append(metricsList, metric)
}
p := &Prometheus{ p := &Prometheus{
MetricsList: metricsList, MetricsList: metricsList,
MetricsPath: defaultMetricPath, MetricsPath: defaultMetricPath,
ReqCntURLLabelMappingFn: func(c *gin.Context) string { 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 // 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) { func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
p.Ppg.PushGatewayURL = pushGatewayURL p.Ppg.PushGatewayURL = pushGatewayURL
p.Ppg.MetricsURL = metricsURL p.Ppg.MetricsURL = metricsURL
@@ -158,13 +159,13 @@ func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushInter
p.startPushTicker() p.startPushTicker()
} }
// SetPushGatewayJob job name, defaults to "gin" // SetPushGatewayJob job name, defaults to "gin".
func (p *Prometheus) SetPushGatewayJob(j string) { func (p *Prometheus) SetPushGatewayJob(j string) {
p.Ppg.Job = j p.Ppg.Job = j
} }
// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the // 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) { func (p *Prometheus) SetListenAddress(address string) {
p.listenAddress = address p.listenAddress = address
if p.listenAddress != "" { 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) { func (p *Prometheus) SetMetricsPath(e *gin.Engine) {
if p.listenAddress != "" { 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) { func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) {
if p.listenAddress != "" { if p.listenAddress != "" {
@@ -205,34 +206,43 @@ func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts
} }
func (p *Prometheus) runServer() { func (p *Prometheus) runServer() {
if p.listenAddress != "" { go p.router.Run(p.listenAddress)
go p.router.Run(p.listenAddress)
}
} }
func (p *Prometheus) getMetrics() []byte { 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() defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
body, _ := io.ReadAll(response.Body)
return body return body
} }
var hostname, _ = os.Hostname()
func (p *Prometheus) getPushGatewayURL() string { func (p *Prometheus) getPushGatewayURL() string {
h, _ := os.Hostname()
if p.Ppg.Job == "" { if p.Ppg.Job == "" {
p.Ppg.Job = "gin" 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) { func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics)) req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics))
if err != nil {
return
}
client := &http.Client{} 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()) fmt.Println("Error sending to push gateway error:", err.Error())
} }
resp.Body.Close()
} }
func (p *Prometheus) startPushTicker() { 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 { func NewMetric(m *Metric, subsystem string) prometheus.Collector {
var metric prometheus.Collector var metric prometheus.Collector
switch m.Type { switch m.Type {
@@ -321,20 +331,20 @@ func NewMetric(m *Metric, subsystem string) prometheus.Collector {
} }
func (p *Prometheus) registerMetrics(subsystem string) { func (p *Prometheus) registerMetrics(subsystem string) {
for _, metricDef := range p.MetricsList { for _, metricDef := range p.MetricsList {
metric := NewMetric(metricDef, subsystem) metric := NewMetric(metricDef, subsystem)
if err := prometheus.Register(metric); err != nil { if err := prometheus.Register(metric); err != nil {
fmt.Println("could not be registered in Prometheus,metricDef.Name:", metricDef.Name, " error:", err.Error()) fmt.Println("could not be registered in Prometheus,metricDef.Name:", metricDef.Name, " error:", err.Error())
} }
switch metricDef { switch metricDef {
case reqCnt: case reqCounter:
p.reqCnt = metric.(*prometheus.CounterVec) p.reqCnt = metric.(*prometheus.CounterVec)
case reqDur: case reqDuration:
p.reqDur = metric.(*prometheus.HistogramVec) p.reqDur = metric.(*prometheus.HistogramVec)
case resSz: case resSize:
p.resSz = metric.(prometheus.Summary) p.resSz = metric.(prometheus.Summary)
case reqSz: case reqSize:
p.reqSz = metric.(prometheus.Summary) p.reqSz = metric.(prometheus.Summary)
} }
metricDef.MetricCollector = metric metricDef.MetricCollector = metric
@@ -353,7 +363,7 @@ func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) {
p.SetMetricsPathWithAuth(e, accounts) p.SetMetricsPathWithAuth(e, accounts)
} }
// HandlerFunc defines handler function for middleware // HandlerFunc defines handler function for middleware.
func (p *Prometheus) HandlerFunc() gin.HandlerFunc { func (p *Prometheus) HandlerFunc() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
if c.Request.URL.Path == p.MetricsPath { if c.Request.URL.Path == p.MetricsPath {
@@ -393,7 +403,7 @@ func prometheusHandler() gin.HandlerFunc {
} }
func computeApproximateRequestSize(r *http.Request) int { func computeApproximateRequestSize(r *http.Request) int {
s := 0 var s int
if r.URL != nil { if r.URL != nil {
s = len(r.URL.Path) s = len(r.URL.Path)
} }

View File

@@ -15,8 +15,10 @@
package kafka package kafka
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"strings"
"time" "time"
"github.com/OpenIMSDK/protocol/constant" "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 = 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.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.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 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 != "" { if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" {
p.config.Net.SASL.Enable = true p.config.Net.SASL.Enable = true
p.config.Net.SASL.User = config.Config.Kafka.Username p.config.Net.SASL.User = config.Config.Kafka.Username

View File

@@ -2,10 +2,11 @@ package prommetrics
import ( import (
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/collectors"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
) )
func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *grpc_prometheus.ServerMetrics, error) { func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *grpc_prometheus.ServerMetrics, error) {

View File

@@ -3,9 +3,10 @@ package prommetrics
import ( import (
"testing" "testing"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
) )
func TestNewGrpcPromObj(t *testing.T) { func TestNewGrpcPromObj(t *testing.T) {

View File

@@ -28,10 +28,11 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/discoveryregistry"
"github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/mw"
"github.com/OpenIMSDK/tools/network" "github.com/OpenIMSDK/tools/network"

View File

@@ -118,8 +118,9 @@ func GetNotificationConversationIDByConversationID(conversationID string) string
l := strings.Split(conversationID, "_") l := strings.Split(conversationID, "_")
if len(l) > 1 { if len(l) > 1 {
l[0] = "n" l[0] = "n"
return conversationID return strings.Join(l, "_")
} }
return "" return ""
} }

View File

@@ -39,7 +39,7 @@ scripts/
├── demo.sh # Demonstration or example script. ├── demo.sh # Demonstration or example script.
├── docker-check-service.sh # Docker script to check services' status. ├── docker-check-service.sh # Docker script to check services' status.
├── docker-start-all.sh # Docker script to start all containers/services. ├── 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. ├── env_check.sh # Environment verification and checking.
├── gen-swagger-docs.sh # Script to generate Swagger documentation. ├── gen-swagger-docs.sh # Script to generate Swagger documentation.
├── genconfig.sh # Generate configuration files. ├── genconfig.sh # Generate configuration files.

View File

@@ -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

View File

@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# 本脚本功能:根据 scripts/environment.sh 配置,生成 OPENIM 组件 YAML 配置文件。 # Function of this script: Generate the OPENIM component YAML configuration file according to the scripts/environment.sh configuration.
# 示例./scripts/genconfig.sh scripts/install/environment.sh scripts/template/config.yaml # 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 # Read: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md
env_file="$1" env_file="$1"
@@ -29,8 +29,17 @@ if [ $# -ne 2 ];then
exit 1 exit 1
fi fi
# Check if the required commands exist
openim::util::require-dig 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}" source "${env_file}"

View File

@@ -31,6 +31,8 @@ readonly ENV_FILE=${ENV_FILE:-"${OPENIM_ROOT}/scripts/install/environment.sh"}
declare -A TEMPLATES=( declare -A TEMPLATES=(
["${OPENIM_ROOT}/deployments/templates/env_template.yaml"]="${OPENIM_ROOT}/.env" ["${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/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 for template in "${!TEMPLATES[@]}"; do
@@ -45,6 +47,7 @@ for template in "${!TEMPLATES[@]}"; do
openim::log::error "Error processing template file ${template}" openim::log::error "Error processing template file ${template}"
exit 1 exit 1
} }
sleep 0.5
done done
done done

View File

@@ -116,7 +116,12 @@ LAST_OCTET=$((LAST_OCTET + 1))
PROMETHEUS_NETWORK_ADDRESS=$(generate_ip) PROMETHEUS_NETWORK_ADDRESS=$(generate_ip)
LAST_OCTET=$((LAST_OCTET + 1)) LAST_OCTET=$((LAST_OCTET + 1))
GRAFANA_NETWORK_ADDRESS=$(generate_ip) 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 配置 ###################### ###################### openim 配置 ######################
# read: https://github.com/openimsdk/open-im-server/blob/main/deployment/README.md # read: https://github.com/openimsdk/open-im-server/blob/main/deployment/README.md
def "OPENIM_DATA_DIR" "/data/openim" 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_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # openim-web的地址
def "OPENIM_WEB_DIST_PATH" "/app/dist" # openim-web的dist路径 def "OPENIM_WEB_DIST_PATH" "/app/dist" # openim-web的dist路径
###################### openim-admin-front 配置信息 ######################
def "OPENIM_ADMIN_FRONT_PORT" "11002" # openim-admin-front的端口
###################### RPC 配置信息 ###################### ###################### RPC 配置信息 ######################
def "RPC_REGISTER_IP" # RPC的注册IP def "RPC_REGISTER_IP" # RPC的注册IP
def "RPC_LISTEN_IP" "0.0.0.0" # 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_PORT" "19090" # Prometheus的端口
def "PROMETHEUS_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # 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 配置信息 ###################### ###################### Grafana 配置信息 ######################
def "GRAFANA_PORT" "3000" # Grafana的端口 def "GRAFANA_PORT" "13000" # Grafana的端口
def "GRAFANA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Grafana的地址 def "GRAFANA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Grafana的地址
###################### RPC Port Configuration Variables ###################### ###################### RPC Port Configuration Variables ######################
@@ -340,8 +378,9 @@ def "IOS_BADGE_COUNT" "true" # IOS徽章计数
def "IOS_PRODUCTION" "false" # IOS生产 def "IOS_PRODUCTION" "false" # IOS生产
###################### Prometheus 配置信息 ###################### ###################### Prometheus 配置信息 ######################
def "PROMETHEUS_ENABLE" "false" # 是否启用 Prometheus # 是否启用 Prometheus
def "PROMETHEUS_URL" "/prometheus" readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'false'}
def "PROMETHEUS_URL" "${GRAFANA_ADDRESS}:${GRAFANA_PORT}"
# Api 服务的 Prometheus 端口 # Api 服务的 Prometheus 端口
readonly API_PROM_PORT=${API_PROM_PORT:-'20100'} readonly API_PROM_PORT=${API_PROM_PORT:-'20100'}
# User 服务的 Prometheus 端口 # User 服务的 Prometheus 端口
@@ -367,6 +406,7 @@ readonly THIRD_PROM_PORT=${THIRD_PROM_PORT:-'21301'}
# Message Transfer 服务的 Prometheus 端口列表 # Message Transfer 服务的 Prometheus 端口列表
readonly MSG_TRANSFER_PROM_PORT=${MSG_TRANSFER_PROM_PORT:-'21400, 21401, 21402, 21403'} 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 ###################### ###################### OpenIM openim-api ######################
def "OPENIM_API_HOST" "127.0.0.1" def "OPENIM_API_HOST" "127.0.0.1"

View File

@@ -22,6 +22,8 @@
# It can be downloaded from the following link: # It can be downloaded from the following link:
# https://github.com/OpenIMSDK/Open-IM-Protoc/releases/tag/v1.0.0 # 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 (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 # Download link (Linux): https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/linux.zip
# #

View File

@@ -76,8 +76,8 @@ function openim::install::install_openim() {
openim::log::info "check openim dependency" openim::log::info "check openim dependency"
openim::common::sudo "cp -r ${OPENIM_ROOT}/config/* ${OPENIM_CONFIG_DIR}/" 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} ${OPENIM_ROOT}/deployments/templates/openim.yaml > ${OPENIM_CONFIG_DIR}/config.yaml
"${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/prometheus.yml > ${OPENIM_CONFIG_DIR}/prometheus.yml
openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]} openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}

View File

@@ -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. # 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' || { systemctl status ${SERVER_NAME}|grep -q 'active' || {
openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly"
return 1 return 1
} }

View File

@@ -38,9 +38,8 @@ IAM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
[[ -z ${COMMON_SOURCED} ]] && source ${IAM_ROOT}/scripts/install/common.sh [[ -z ${COMMON_SOURCED} ]] && source ${IAM_ROOT}/scripts/install/common.sh
# API Server API Address:Port # 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} INSECURE_OPENIMAUTO=${OPENIM_RPC_AUTH_HOST}:${OPENIM_AUTH_PORT}
CCURL="curl -f -s -XPOST" # Create CCURL="curl -f -s -XPOST" # Create
UCURL="curl -f -s -XPUT" # Update UCURL="curl -f -s -XPUT" # Update
RCURL="curl -f -s -XGET" # Retrieve RCURL="curl -f -s -XGET" # Retrieve
@@ -73,7 +72,7 @@ function openim::test::auth() {
# Define a function to get a token (Admin Token) # Define a function to get a token (Admin Token)
openim::test::get_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"}') -d'{"secret": "'"$SECRET"'","platformID": 1,"userID": "openIM123456"}')
token=$(echo $token_response | grep -Po 'token[" :]+\K[^"]+') token=$(echo $token_response | grep -Po 'token[" :]+\K[^"]+')
echo "$token" echo "$token"
@@ -94,7 +93,7 @@ EOF
) )
echo "Requesting force logout for user: $request_body" 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" openim::test::check_error "$response"
} }
@@ -127,7 +126,7 @@ EOF
echo "Request body for user registration: $request_body" echo "Request body for user registration: $request_body"
# Send the registration request # 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 # Check for errors in the response
openim::test::check_error "$user_register_response" openim::test::check_error "$user_register_response"
@@ -148,7 +147,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -166,7 +165,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -184,7 +183,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -204,7 +203,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -223,7 +222,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -238,7 +237,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -259,7 +258,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -275,7 +274,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -330,7 +329,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -346,7 +345,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -365,7 +364,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -384,7 +383,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -401,7 +400,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -419,7 +418,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -437,7 +436,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -456,7 +455,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -475,7 +474,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -491,7 +490,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -507,7 +506,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -526,7 +525,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -619,7 +618,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -639,7 +638,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -656,7 +655,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -671,7 +670,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -690,7 +689,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -706,7 +705,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -725,7 +724,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -744,7 +743,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -769,7 +768,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -784,7 +783,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -799,7 +798,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -814,7 +813,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -830,7 +829,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -848,7 +847,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -873,7 +872,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -890,7 +889,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -909,7 +908,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -927,7 +926,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -946,7 +945,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -963,7 +962,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -981,7 +980,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -1078,7 +1077,7 @@ function openim::test::group() {
# Define a function to register a user # Define a function to register a user
openim::register_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'{ -d'{
"secret": "openIM123", "secret": "openIM123",
"users": [{"userID": "11111112","nickname": "yourNickname","faceURL": "yourFaceURL"}] "users": [{"userID": "11111112","nickname": "yourNickname","faceURL": "yourFaceURL"}]
@@ -1090,7 +1089,7 @@ openim::register_user() {
# Define a function to check the account # Define a function to check the account
openim::test::check_account() { openim::test::check_account() {
local token=$1 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'{ -d'{
"checkUserIDs": ["11111111","11111112"] "checkUserIDs": ["11111111","11111112"]
}') }')
@@ -1164,7 +1163,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -1185,7 +1184,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }
@@ -1203,7 +1202,7 @@ EOF
) )
echo "$request_body" 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" openim::test::check_error "$response"
} }

View File

@@ -1121,8 +1121,7 @@ function openim::util::check-file-in-alphabetical-order {
# Checks whether jq is installed. # Checks whether jq is installed.
function openim::util::require-jq { function openim::util::require-jq {
if ! command -v jq &>/dev/null; then if ! command -v jq &>/dev/null; then
echo "jq not found. Please install." 1>&2 openim::log::errexit "jq not found. Please install." 1>&2
return 1
fi fi
} }
@@ -1130,15 +1129,10 @@ function openim::util::require-jq {
# Checks whether dig is installed and provides installation instructions if it is not. # Checks whether dig is installed and provides installation instructions if it is not.
function openim::util::require-dig { function openim::util::require-dig {
if ! command -v dig &>/dev/null; then if ! command -v dig &>/dev/null; then
echo "dig command not found." openim::log::error "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/"
return 1 return 1
fi fi
return 0
} }
# outputs md5 hash of $1, works on macOS and Linux # outputs md5 hash of $1, works on macOS and Linux

View File

@@ -34,7 +34,7 @@ release.tag: tools.verify.gsemver release.ensure-tag
## release.ensure-tag: ensure tag ## release.ensure-tag: ensure tag
.PHONY: release.ensure-tag .PHONY: release.ensure-tag
release.ensure-tag: tools.verify.gsemver release.ensure-tag: tools.verify.gsemver
@scripts/ensure_tag.sh @scripts/ensure-tag.sh
## release.help: Display help information about the release package ## release.help: Display help information about the release package
.PHONY: release.help .PHONY: release.help

View File

@@ -1 +0,0 @@
.keep

View File

@@ -1,7 +1,10 @@
package user package user
import ( import (
"fmt"
gettoken "github.com/openimsdk/open-im-server/v3/test/e2e/api/token" 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 // 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 // GetUsersInfo retrieves detailed information for a list of user IDs
func GetUsersInfo(token string, userIDs []string) error { 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{ requestBody := UserInfoRequest{
UserIDs: userIDs, 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 // UpdateUserInfo updates the information for a user
func UpdateUserInfo(token, userID, nickname, faceURL string) error { 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{ requestBody := UserInfoRequest{
UserInfo: &gettoken.User{ UserInfo: &gettoken.User{
UserID: userID, UserID: userID,
@@ -32,13 +41,17 @@ func UpdateUserInfo(token, userID, nickname, faceURL string) error {
FaceURL: faceURL, 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 // GetUsersOnlineStatus retrieves the online status for a list of user IDs
func GetUsersOnlineStatus(token string, userIDs []string) error { 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{ requestBody := GetUsersOnlineStatusRequest{
UserIDs: userIDs, UserIDs: userIDs,
} }
return sendPostRequestWithToken("http://your-api-host:port/user/get_users_online_status", token, requestBody)
} return sendPostRequestWithToken(url, token, requestBody)
}

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
gettoken "github.com/openimsdk/open-im-server/v3/test/e2e/api/token" 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 // ForceLogoutRequest represents a request to force a user logout
@@ -34,30 +35,39 @@ type Pagination struct {
// ForceLogout forces a user to log out // ForceLogout forces a user to log out
func ForceLogout(token, userID string, platformID int) error { 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{ requestBody := ForceLogoutRequest{
PlatformID: platformID, PlatformID: platformID,
UserID: userID, 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 // CheckUserAccount checks if the user accounts exist
func CheckUserAccount(token string, userIDs []string) error { func CheckUserAccount(token string, userIDs []string) error {
url := fmt.Sprintf("http://%s:%s/user/account_check", config.LoadConfig().APIHost, config.LoadConfig().APIPort)
requestBody := CheckUserAccountRequest{ requestBody := CheckUserAccountRequest{
CheckUserIDs: userIDs, 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 // GetUsers retrieves a list of users with pagination
func GetUsers(token string, pageNumber, showNumber int) error { 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{ requestBody := GetUsersRequest{
Pagination: Pagination{ Pagination: Pagination{
PageNumber: pageNumber, PageNumber: pageNumber,
ShowNumber: showNumber, 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 // sendPostRequestWithToken sends a POST request with a token in the header

View File

@@ -20,4 +20,4 @@ func TestMain(m *testing.M) {
func TestE2E(t *testing.T) { func TestE2E(t *testing.T) {
RunE2ETests(t) RunE2ETests(t)
} }

View File

@@ -1 +0,0 @@
.keep

View File

@@ -1,6 +1,9 @@
package config package config
import "flag" import (
"flag"
"os"
)
// Flags is the flag set that AddOptions adds to. Test authors should // Flags is the flag set that AddOptions adds to. Test authors should
// also use it instead of directly adding to the global command line. // 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) 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
}