mirror of
https://github.com/openimsdk/open-im-server.git
synced 2025-12-04 03:12:19 +08:00
Merge remote-tracking branch 'upstream/main'
# Conflicts: # pkg/common/db/controller/msg.go
This commit is contained in:
commit
695d30ac74
4
.env
4
.env
@ -29,8 +29,8 @@ PASSWORD=openIM123
|
||||
MINIO_ENDPOINT=http://172.28.0.1:10005
|
||||
|
||||
# Base URL for the application programming interface (API).
|
||||
# Default: API_URL=http://172.28.0.1:10002
|
||||
API_URL=http://172.28.0.1:10002
|
||||
# Default: API_URL=http://172.0.0.1:10002
|
||||
API_URL=http://172.0.0.1:10002
|
||||
|
||||
# Directory path for storing data files or related information.
|
||||
# Default: DATA_DIR=./
|
||||
|
||||
18
.github/.codecov.yml
vendored
18
.github/.codecov.yml
vendored
@ -20,4 +20,20 @@ coverage:
|
||||
paths:
|
||||
- pkg/* # only include coverage in "pkg/" folder
|
||||
informational: true # Always pass check
|
||||
patch: off # disable the commit only checks
|
||||
tools: # declare a new status context "tools"
|
||||
paths:
|
||||
- tools/* # only include coverage in "tools/" folder
|
||||
informational: true # Always pass check
|
||||
test: # declare a new status context "test"
|
||||
paths:
|
||||
- test/* # only include coverage in "test/" folder
|
||||
informational: true # Always pass check
|
||||
# internal: # declare a new status context "internal"
|
||||
# paths:
|
||||
# - internal/* # only include coverage in "internal/" folder
|
||||
# informational: true # Always pass check
|
||||
# cmd: # declare a new status context "cmd"
|
||||
# paths:
|
||||
# - cmd/* # only include coverage in "cmd/" folder
|
||||
# informational: true # Always pass check
|
||||
patch: off # disable the commit only checks
|
||||
|
||||
90
.github/workflows/api-test.yml
vendored
Normal file
90
.github/workflows/api-test.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: OpenIM API TEST
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "CONTRIBUTING.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "docs/**"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.19"
|
||||
GOLANGCI_VERSION: "v1.50.1"
|
||||
|
||||
jobs:
|
||||
execute-linux-systemd-scripts:
|
||||
name: Execute OpenIM script on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
environment:
|
||||
name: openim
|
||||
strategy:
|
||||
matrix:
|
||||
go_version: ["1.20"]
|
||||
os: ["ubuntu-latest"]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go ${{ matrix.go_version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
id: go
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker Operations
|
||||
run: |
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/OpenIMSDK/openim-docker/main/example/basic-openim-server-dependency.yml
|
||||
sudo docker compose up -d
|
||||
sudo sleep 60
|
||||
|
||||
- name: Module Operations
|
||||
run: |
|
||||
sudo make tidy
|
||||
sudo make tools.verify.go-gitlint
|
||||
|
||||
- name: Build, Start, Check Services and Print Logs
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -i && \
|
||||
sudo ./scripts/install/install.sh -s && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
|
||||
- name: Run Test
|
||||
run: |
|
||||
sudo make test-api && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
|
||||
- name: Stop Services
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -u && \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
12
.github/workflows/build-docker-image.yml
vendored
12
.github/workflows/build-docker-image.yml
vendored
@ -52,6 +52,7 @@ jobs:
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
@ -87,6 +88,16 @@ jobs:
|
||||
uses: docker/metadata-action@v5.0.0
|
||||
with:
|
||||
images: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Log in to AliYun Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
@ -126,6 +137,7 @@ jobs:
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
14
.github/workflows/create_branch_on_tag.yml
vendored
14
.github/workflows/create_branch_on_tag.yml
vendored
@ -1,3 +1,17 @@
|
||||
# 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: Create Branch on Tag
|
||||
|
||||
on:
|
||||
|
||||
60
.github/workflows/e2e-test.yml
vendored
60
.github/workflows/e2e-test.yml
vendored
@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: OpenIM E2E Test
|
||||
name: OpenIM Linux System E2E Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@ -41,4 +41,60 @@ jobs:
|
||||
|
||||
- name: Create e2e test
|
||||
run: |
|
||||
echo "...test e2e"
|
||||
echo "...test e2e"
|
||||
|
||||
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
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -i
|
||||
|
||||
- name: Exec OpenIM System Status Chack
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -s
|
||||
|
||||
- name: Exec OpenIM API test
|
||||
run: |
|
||||
sudo make test-api
|
||||
|
||||
- name: Exec OpenIM E2E test
|
||||
run: |
|
||||
sudo make test-e2e
|
||||
|
||||
- name: Exec OpenIM System uninstall
|
||||
run: |
|
||||
sudo ./scripts/install/install.sh -u
|
||||
33
.github/workflows/openimci.yml
vendored
33
.github/workflows/openimci.yml
vendored
@ -64,7 +64,8 @@ jobs:
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: 2.x
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Module Operations
|
||||
run: |
|
||||
@ -122,7 +123,8 @@ jobs:
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: 2.x
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run OpenIM make install start
|
||||
run: |
|
||||
sudo make install
|
||||
@ -145,10 +147,13 @@ jobs:
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
id: go
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: 2.x
|
||||
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
|
||||
@ -160,18 +165,15 @@ jobs:
|
||||
sudo make tidy
|
||||
sudo make tools.verify.go-gitlint
|
||||
|
||||
- name: Build, Start and Check Services
|
||||
- name: Build, Start, Check Services and Print Logs
|
||||
run: |
|
||||
sudo make init
|
||||
sudo make build
|
||||
sudo make start
|
||||
sudo make check
|
||||
sudo make init && \
|
||||
sudo make build && \
|
||||
sudo make start && \
|
||||
sudo make check || \
|
||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||
|
||||
- name: Print OpenIM Logs
|
||||
run: sudo cat ./_output/logs/* 2>/dev/null
|
||||
continue-on-error: true
|
||||
|
||||
openim-build-image:
|
||||
openim-test-build-image:
|
||||
name: Build OpenIM Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
@ -184,10 +186,13 @@ jobs:
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
id: go
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
version: 2.x
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Test Docker Build
|
||||
run: |
|
||||
sudo make image
|
||||
@ -20,7 +20,7 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## OpenIM versioning policy
|
||||
|
||||
+ [OpenIM Version](../docs/conversions/version.md)
|
||||
+ [OpenIM Version](../docs/contrib/version.md)
|
||||
|
||||
## command
|
||||
|
||||
|
||||
@ -42,6 +42,12 @@ If you are familiar with [Makefile](./Makefile) , you can easily see the clever
|
||||
|
||||
The [Makefile](./Makefile) is for every developer, even if you don't know how to use the Makefile tool, don't worry, we provide two great commands to get you up to speed with the Makefile architecture, `make help` and `make help-all`, it can reduce problems of the developing environment.
|
||||
|
||||
In accordance with the naming conventions adopted by OpenIM and drawing reference from the Google Naming Conventions as per the guidelines available at https://google.github.io/styleguide/go/, the following expectations for naming practices within the project are set forth:
|
||||
|
||||
+ https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md
|
||||
+ https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md
|
||||
|
||||
|
||||
## Code of ConductCode of Conduct
|
||||
|
||||
#### Code and doc contribution
|
||||
@ -270,7 +276,7 @@ You can find some very formal PR in [RFC](https://github.com/openimsdk/open-im-s
|
||||
**🈴 Reviewing PRs:**
|
||||
|
||||
+ Be respectful and constructive
|
||||
+ Assign yourself to the PR
|
||||
+ Assign yourself to the PR (comment `/assign`)
|
||||
+ Check if all checks are passing
|
||||
+ Suggest changes instead of simply commenting on found issues
|
||||
+ If you are unsure about something, ask the author
|
||||
|
||||
11
Makefile
11
Makefile
@ -6,7 +6,7 @@
|
||||
|
||||
## all: Run tidy, gen, add-copyright, format, lint, cover, build ✨
|
||||
.PHONY: all
|
||||
all: tidy gen add-copyright verify lint cover restart
|
||||
all: tidy gen add-copyright verify test-api lint cover restart
|
||||
|
||||
# ==============================================================================
|
||||
# Build set
|
||||
@ -166,6 +166,15 @@ test:
|
||||
cover:
|
||||
@$(MAKE) go.test.cover
|
||||
|
||||
## test-api: Run api test. ✨
|
||||
.PHONY: test-api
|
||||
test-api:
|
||||
@$(MAKE) go.test.api
|
||||
|
||||
## test-e2e: Run e2e test
|
||||
test-e2e:
|
||||
@$(MAKE) go.test.e2e
|
||||
|
||||
## updates: Check for updates to go.mod dependencies. ✨
|
||||
.PHONY: updates
|
||||
@$(MAKE) go.updates
|
||||
|
||||
@ -90,22 +90,22 @@ OpenIM 我们的目标是建立一个顶级的开源社区。我们有一套标
|
||||
|
||||
在开始之前,请确保你的更改是有需求的。最好的方法是创建一个[新的讨论](https://github.com/openimsdk/open-im-server/discussions/new/choose) 或 [Slack 通信](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q),或者如果你发现一个问题,首先[报告它](https://github.com/openimsdk/open-im-server/issues/new/choose)。
|
||||
|
||||
+ [代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/go_code.md)
|
||||
+ [代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md)
|
||||
|
||||
+ [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md)
|
||||
+ [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
|
||||
|
||||
+ [目录标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/directory.md)
|
||||
+ [目录标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md)
|
||||
|
||||
+ [提交标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/commit.md)
|
||||
+ [提交标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md)
|
||||
|
||||
+ [版本控制标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md)
|
||||
+ [版本控制标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md)
|
||||
|
||||
+ [接口标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/interface.md)
|
||||
+ [接口标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/interface.md)
|
||||
|
||||
+ [OpenIM配置和环境变量设置](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md)
|
||||
|
||||
> **Note**
|
||||
> 针对中国的用户,阅读我们的 [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md) 以便使用国内 aliyun 的镜像地址。OpenIM 也有针对中国的 gitee 同步仓库,你可以在 [gitee.com](https://gitee.com/openimsdk) 上找到它。
|
||||
> 针对中国的用户,阅读我们的 [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) 以便使用国内 aliyun 的镜像地址。OpenIM 也有针对中国的 gitee 同步仓库,你可以在 [gitee.com](https://gitee.com/openimsdk) 上找到它。
|
||||
|
||||
## :link: 链接
|
||||
|
||||
|
||||
79
README.md
79
README.md
@ -4,30 +4,27 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3 align="center" style="border-bottom: none">
|
||||
⭐️ Open source Instant Messaging Server ⭐️ <br>
|
||||
<h3>
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/openimsdk/open-im-server/stargazers)
|
||||
[](https://github.com/openimsdk/open-im-server/network/members)
|
||||
[](https://app.codecov.io/gh/openimsdk/open-im-server)
|
||||
[](https://goreportcard.com/report/github.com/openimsdk/open-im-server)
|
||||
[](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3)
|
||||
[](https://github.com/openimsdk/open-im-server/blob/main/LICENSE)
|
||||
[](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)
|
||||
[](https://www.bestpractices.dev/projects/8045)
|
||||
[](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22)
|
||||
[](https://golang.org/)
|
||||
|
||||
<p align=center>
|
||||
<a href="https://goreportcard.com/report/github.com/openimsdk/open-im-server"><img src="https://goreportcard.com/badge/github.com/openimsdk/open-im-server" alt="A+"></a>
|
||||
<a href="https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22"><img src="https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?logo=%22github%22" alt="good first"></a>
|
||||
<a href="https://github.com/openimsdk/open-im-server"><img src="https://img.shields.io/github/stars/openimsdk/open-im-server.svg?style=flat&logo=github&colorB=deeppink&label=stars"></a>
|
||||
<a href="https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q"><img src="https://img.shields.io/badge/Slack-300%2B-blueviolet?logo=slack&logoColor=white"></a>
|
||||
<a href="https://github.com/openimsdk/open-im-server/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
|
||||
<a href="https://golang.org/"><img src="https://img.shields.io/badge/Language-Go-blue.svg"></a>
|
||||
<a href="https://pkg.go.dev/github.com/openimsdk/open-im-server/v3"><img src="https://pkg.go.dev/badge/github.com/openimsdk/open-im-server/v3.svg" alt="Go Reference"></a>
|
||||
</p>
|
||||
[**English**](./README.md) •
|
||||
[**简体中文**](./README-zh_CN.md) •
|
||||
[**Docs**](https://openim.io/en)
|
||||
|
||||
</div>
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="./README.md"><b> English </b></a> •
|
||||
<a href="./README-zh_CN.md"><b> 简体中文 </b></a> •
|
||||
<a href="https://openim.io/en"><b> Docs </b></a>
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
## Ⓜ️ About OpenIM
|
||||
|
||||
@ -118,7 +115,7 @@ It is recommended to use Docker Compose for deployment, which can easily and qui
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> If you don't know OpenIM's versioning policy, 📚Read our release policy: https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md
|
||||
> If you don't know OpenIM's versioning policy, 📚Read our release policy: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
|
||||
|
||||
|
||||
</details>
|
||||
@ -133,7 +130,7 @@ Ur need `Go 1.20` or higher version, and `make`.
|
||||
go version && make --version || echo "Error: One of the commands failed."
|
||||
```
|
||||
|
||||
Version Details: https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md
|
||||
Version Details: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
|
||||
|
||||
You can get the version number from the command below or from [github releases](https://github.com/openimsdk/open-im-server/tags).
|
||||
|
||||
@ -141,7 +138,7 @@ You can get the version number from the command below or from [github releases](
|
||||
$ curl --silent "https://api.github.com/repos/openimsdk/open-im-server/releases" | jq -r '.[].tag_name'
|
||||
```
|
||||
|
||||
We have our own version management policy, if you are interested in our version management, I recommend reading [📚 OpenIM Version](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md), We recommend using stable versions such as `v3.3.0` and `v3.2.0` whenever possible. `v3.1.1-alpha.3` as well as `v3.3.0-beta.0` and `v3.2.0-rc.0` are pre-release or beta versions and are not recommended.
|
||||
We have our own version management policy, if you are interested in our version management, I recommend reading [📚 OpenIM Version](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md), We recommend using stable versions such as `v3.3.0` and `v3.2.0` whenever possible. `v3.1.1-alpha.3` as well as `v3.3.0-beta.0` and `v3.2.0-rc.0` are pre-release or beta versions and are not recommended.
|
||||
|
||||
Set `OPENIM_VERSION` environment variables for the latest `OPENIM_VERSION` number, or replace the `OPENIM_VERSION` for you to install the OpenIM-Server `OPENIM_VERSION`:
|
||||
|
||||
@ -199,21 +196,41 @@ Delve into the heart of Open-IM-Server's functionality with our architecture dia
|
||||
|
||||
## :hammer_and_wrench: To start developing OpenIM
|
||||
|
||||
[](https://vscode.dev/github/openimsdk/open-im-server)
|
||||
|
||||
OpenIM Our goal is to build a top-level open source community. We have a set of standards, in the [Community repository](https://github.com/OpenIMSDK/community).
|
||||
|
||||
If you'd like to contribute to this Open-IM-Server repository, please read our [contributor documentation](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md).
|
||||
|
||||
Before you start, please make sure your changes are in demand. The best for that is to create a [new discussion](https://github.com/openimsdk/open-im-server/discussions/new/choose) OR [Slack Communication](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), or if you find an issue, [report it](https://github.com/openimsdk/open-im-server/issues/new/choose) first.
|
||||
|
||||
- [Code Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/go_code.md)
|
||||
- [Docker Images Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md)
|
||||
- [Directory Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/directory.md)
|
||||
- [Commit Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/commit.md)
|
||||
- [Versioning Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md)
|
||||
- [Interface Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/api.md)
|
||||
- [Log Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/logging.md)
|
||||
- [Error Code Standards](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/error_code.md)
|
||||
- [OpenIM configuration and environment variable Settings](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md)
|
||||
- [OpenIM API Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md)
|
||||
- [OpenIM Bash Logging](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md)
|
||||
- [OpenIM CI/CD Actions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md)
|
||||
- [OpenIM Code Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md)
|
||||
- [OpenIM Commit Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md)
|
||||
- [OpenIM Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md)
|
||||
- [OpenIM Directory Structure](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md)
|
||||
- [OpenIM Environment Setup](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md)
|
||||
- [OpenIM Error Code Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md)
|
||||
- [OpenIM Git Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md)
|
||||
- [OpenIM Git Cherry Pick Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md)
|
||||
- [OpenIM GitHub Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md)
|
||||
- [OpenIM Go Code Standards](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md)
|
||||
- [OpenIM Image Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md)
|
||||
- [OpenIM Initial Configuration](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md)
|
||||
- [OpenIM Docker Installation Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md)
|
||||
- [OpenIM OpenIM Linux System Installation](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md)
|
||||
- [OpenIM Linux Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md)
|
||||
- [OpenIM Local Actions Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md)
|
||||
- [OpenIM Logging Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md)
|
||||
- [OpenIM Offline Deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md)
|
||||
- [OpenIM Protoc Tools](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md)
|
||||
- [OpenIM Testing Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md)
|
||||
- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.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 Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md)
|
||||
|
||||
|
||||
## :busts_in_silhouette: Community
|
||||
|
||||
@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
FROM BASE_IMAGE
|
||||
|
||||
WORKDIR ${SERVER_WORKDIR}
|
||||
|
||||
@ -17,13 +17,12 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
ginProm "github.com/openimsdk/open-im-server/v3/pkg/common/ginPrometheus"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
"net"
|
||||
_ "net/http/pprof"
|
||||
"strconv"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/discovery_register"
|
||||
ginProm "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
@ -33,6 +32,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -65,7 +65,7 @@ func run(port int, proPort int) error {
|
||||
var client discoveryregistry.SvcDiscoveryRegistry
|
||||
|
||||
// Determine whether zk is passed according to whether it is a clustered deployment
|
||||
client, err = discovery_register.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
client, err = kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
if err != nil {
|
||||
log.ZError(context.Background(), "Failed to initialize discovery register", err)
|
||||
|
||||
@ -86,7 +86,7 @@ func run(port int, proPort int) error {
|
||||
router := api.NewGinRouter(client, rdb)
|
||||
//////////////////////////////
|
||||
if config.Config.Prometheus.Enable {
|
||||
p := ginProm.NewPrometheus("app", prom_metrics.GetGinCusMetrics("Api"))
|
||||
p := ginProm.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
|
||||
p.SetListenAddress(fmt.Sprintf(":%d", proPort))
|
||||
p.Use(router)
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ rpcRegisterName:
|
||||
# Whether to output in json format
|
||||
# Whether to include stack trace in logs
|
||||
log:
|
||||
storageLocation: ../logs/
|
||||
storageLocation: ./logs/
|
||||
rotationTime: 24
|
||||
remainRotationCount: 2
|
||||
remainLogLevel: 6
|
||||
|
||||
@ -106,7 +106,7 @@ $ helm repo add brigade https://openimsdk.github.io/openim-charts
|
||||
|
||||
### OpenIM Image Strategy
|
||||
|
||||
Automated offerings include aliyun, ghcr, docker hub: [Image Documentation](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md)
|
||||
Automated offerings include aliyun, ghcr, docker hub: [Image Documentation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
|
||||
|
||||
**Local Test Build Method:**
|
||||
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@ -51,10 +65,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -48,10 +48,10 @@ spec:
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- mountPath: /config/config.yaml
|
||||
- mountPath: /openim/openim-server/config/config.yaml
|
||||
name: config
|
||||
subPath: config.yaml
|
||||
- mountPath: /config/notification.yaml
|
||||
- mountPath: /openim/openim-server/config/
|
||||
name: config
|
||||
subPath: notification.yaml
|
||||
volumes:
|
||||
|
||||
@ -89,11 +89,11 @@ services:
|
||||
ports:
|
||||
- "${KAFKA_PORT}:9094"
|
||||
volumes:
|
||||
- ./scripts/create_topic.sh:/opt/bitnami/kafka/create_topic.sh
|
||||
- ./scripts/create-topic.sh:/opt/bitnami/kafka/create-topic.sh
|
||||
- ${DATA_DIR}/components/kafka:/bitnami/kafka
|
||||
command: >
|
||||
bash -c "
|
||||
/opt/bitnami/scripts/kafka/run.sh & sleep 5; /opt/bitnami/kafka/create_topic.sh; wait
|
||||
/opt/bitnami/scripts/kafka/run.sh & sleep 5; /opt/bitnami/kafka/create-topic.sh; wait
|
||||
"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
@ -142,67 +142,3 @@ services:
|
||||
server:
|
||||
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS}
|
||||
|
||||
# openim-server:
|
||||
# # image: ghcr.io/openimsdk/openim-server:main
|
||||
# # image: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server:main
|
||||
# # image: openim/openim-server:main
|
||||
# image: ${IMAGE_REGISTRY}/openim-server:main
|
||||
# # build: .
|
||||
# container_name: openim-server
|
||||
# ports:
|
||||
# - ${OPENIM_WS_PORT}:10001
|
||||
# - ${API_OPENIM_PORT}:10002
|
||||
# healthcheck:
|
||||
# test: ["CMD", "/openim/openim-server/scripts/check-all.sh"]
|
||||
# interval: 300s
|
||||
# timeout: 10s
|
||||
# retries: 5
|
||||
# volumes:
|
||||
# - ./logs:/openim/openim-server/logs
|
||||
# - ./_output/logs:/openim/openim-server/_output/logs
|
||||
# - ./config:/openim/openim-server/config
|
||||
# - ./scripts:/openim/openim-server/scripts
|
||||
# restart: always
|
||||
# depends_on:
|
||||
# - kafka
|
||||
# - mysql
|
||||
# - mongodb
|
||||
# - redis
|
||||
# - minio
|
||||
# logging:
|
||||
# driver: json-file
|
||||
# options:
|
||||
# max-size: "1g"
|
||||
# max-file: "2"
|
||||
# networks:
|
||||
# server:
|
||||
# ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS}
|
||||
|
||||
# prometheus:
|
||||
# image: prom/prometheus
|
||||
# volumes:
|
||||
# - ./.docker-compose_cfg/prometheus-compose.yml:/etc/prometheus/prometheus.yml
|
||||
# container_name: prometheus
|
||||
# ports:
|
||||
# - ${PROMETHEUS_PORT}:9091
|
||||
# depends_on:
|
||||
# - openim-server
|
||||
# command: --web.listen-address=:9091 --config.file="/etc/prometheus/prometheus.yml"
|
||||
# networks:
|
||||
# openim-server:
|
||||
# ipv4_address: ${PROMETHEUS_NETWORK_ADDRESS}
|
||||
|
||||
# grafana:
|
||||
# image: grafana/grafana
|
||||
# volumes:
|
||||
# - ./.docker-compose_cfg/datasource-compose.yaml:/etc/grafana/provisioning/datasources/datasource.yaml
|
||||
# - ./.docker-compose_cfg/grafana.ini:/etc/grafana/grafana.ini
|
||||
# - ./.docker-compose_cfg/node-exporter-full_rev1.json:/var/lib/grafana/dashboards/node-exporter-full_rev1.json
|
||||
# container_name: grafana
|
||||
# ports:
|
||||
# - ${GRAFANA_PORT}:3000
|
||||
# depends_on:
|
||||
# - prometheus
|
||||
# networks:
|
||||
# openim-server:
|
||||
# ipv4_address: ${GRAFANA_NETWORK_ADDRESS}
|
||||
|
||||
@ -5,7 +5,7 @@ Welcome to the OpenIM Documentation hub! This center provides a comprehensive ra
|
||||
## Table of Contents
|
||||
|
||||
1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Guidance on contributing and configurations for developers
|
||||
2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions) - Coding conventions, logging policies, and other transformation tools
|
||||
2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Coding conventions, logging policies, and other transformation tools
|
||||
|
||||
------
|
||||
|
||||
@ -13,34 +13,34 @@ Welcome to the OpenIM Documentation hub! This center provides a comprehensive ra
|
||||
|
||||
This section offers developers a detailed guide on how to contribute code, set up their environment, and follow the associated processes.
|
||||
|
||||
- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code_conventions.md) - Rules and conventions for writing code in OpenIM.
|
||||
- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Rules and conventions for writing code in OpenIM.
|
||||
- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - A guide on how to carry out development within OpenIM.
|
||||
- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git_cherry-pick.md) - Guidelines on cherry-picking operations.
|
||||
- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git_workflow.md) - The git workflow in OpenIM.
|
||||
- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init_config.md) - Guidance on setting up and initializing OpenIM.
|
||||
- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install_docker.md) - How to install Docker on your machine.
|
||||
- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux_development.md) - Guide to set up the development environment on Linux.
|
||||
- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local_actions.md) - Guidelines on how to carry out certain common actions locally.
|
||||
- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Guidelines on cherry-picking operations.
|
||||
- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - The git workflow in OpenIM.
|
||||
- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Guidance on setting up and initializing OpenIM.
|
||||
- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - How to install Docker on your machine.
|
||||
- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide to set up the development environment on Linux.
|
||||
- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Guidelines on how to carry out certain common actions locally.
|
||||
- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Methods of deploying OpenIM offline.
|
||||
- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc_tools.md) - Guide on using protoc tools.
|
||||
- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util_go.md) - Tools and libraries in OpenIM for Go.
|
||||
- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util_makefile.md) - Best practices and tools for Makefile.
|
||||
- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util_scripts.md) - Best practices and tools for scripts.
|
||||
- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guide on using protoc tools.
|
||||
- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Tools and libraries in OpenIM for Go.
|
||||
- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Best practices and tools for Makefile.
|
||||
- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Best practices and tools for scripts.
|
||||
|
||||
## Conversions
|
||||
|
||||
This section introduces various conventions and policies within OpenIM, encompassing code, logs, versions, and more.
|
||||
|
||||
- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/api.md) - Guidelines and methods for API conversions.
|
||||
- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/bash_log.md) - Logging policies and conventions in OpenIM.
|
||||
- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/cicd_actions.md) - Procedures and conventions for CI/CD.
|
||||
- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/commit.md) - Conventions for code commits in OpenIM.
|
||||
- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/directory.md) - Directory structure and conventions within OpenIM.
|
||||
- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/error_code.md) - List and descriptions of error codes.
|
||||
- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/go_code.md) - Conventions and conversions for Go code.
|
||||
- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md) - Management strategies for OpenIM Docker images, spanning multiple architectures and image repositories.
|
||||
- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/logging.md) - Further detailed conventions on logging.
|
||||
- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md) - Naming and management strategies for OpenIM versions.
|
||||
- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Guidelines and methods for API conversions.
|
||||
- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Logging policies and conventions in OpenIM.
|
||||
- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedures and conventions for CI/CD.
|
||||
- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Conventions for code commits in OpenIM.
|
||||
- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Directory structure and conventions within OpenIM.
|
||||
- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - List and descriptions of error codes.
|
||||
- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Conventions and conversions for Go code.
|
||||
- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Management strategies for OpenIM Docker images, spanning multiple architectures and image repositories.
|
||||
- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Further detailed conventions on logging.
|
||||
- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Naming and management strategies for OpenIM versions.
|
||||
|
||||
|
||||
## For Developers, Contributors, and Community Maintainers
|
||||
@ -49,7 +49,7 @@ This section introduces various conventions and policies within OpenIM, encompas
|
||||
|
||||
If you're a developer or someone keen on contributing:
|
||||
|
||||
- Familiarize yourself with our [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code_conventions.md) and [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git_workflow.md) to ensure smooth contributions.
|
||||
- Familiarize yourself with our [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) and [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) to ensure smooth contributions.
|
||||
- Dive into the [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) to get a hang of the development practices in OpenIM.
|
||||
|
||||
### Community Maintainers
|
||||
@ -57,11 +57,11 @@ If you're a developer or someone keen on contributing:
|
||||
As a community maintainer:
|
||||
|
||||
- Ensure that contributions align with the standards outlined in our documentation.
|
||||
- Regularly review the [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/bash_log.md) and [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/error_code.md) to stay updated.
|
||||
- Regularly review the [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) and [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) to stay updated.
|
||||
|
||||
## For Users
|
||||
|
||||
Users should pay particular attention to:
|
||||
|
||||
- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install_docker.md) - Necessary if you're planning to use Docker images of OpenIM.
|
||||
- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md) - To understand the different images available and how to choose the right one for your architecture.
|
||||
- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necessary if you're planning to use Docker images of OpenIM.
|
||||
- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - To understand the different images available and how to choose the right one for your architecture.
|
||||
42
docs/contrib/README.md
Normal file
42
docs/contrib/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Contrib Documentation Index
|
||||
|
||||
## 📚 General Information
|
||||
- [📄 README](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/README.md) - General introduction to the contribution documentation.
|
||||
- [📑 Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) - Guidelines for setting up a development environment.
|
||||
|
||||
## 🛠 Setup and Installation
|
||||
- [🌍 Environment Setup](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) - Instructions on setting up the development environment.
|
||||
- [🐳 Docker Installation Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) - Steps to install Docker for container management.
|
||||
- [🔧 OpenIM Linux System Installation](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) - Guide for installing OpenIM on a Linux system.
|
||||
|
||||
## 💻 Development Practices
|
||||
- [👨💻 Code Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) - Coding standards to follow for consistency.
|
||||
- [📐 Directory Structure](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) - Explanation of the repository's directory layout.
|
||||
- [🔀 Git Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) - The workflow for using Git in this project (note the file extension error).
|
||||
- [💾 GitHub Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) - Workflow guidelines for GitHub.
|
||||
|
||||
## 🧪 Testing and Deployment
|
||||
- [⚙️ CI/CD Actions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) - Continuous integration and deployment configurations.
|
||||
- [🚀 Offline Deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) - How to deploy the application offline.
|
||||
|
||||
## 🔧 Utilities and Tools
|
||||
- [📦 Protoc Tools](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) - Protobuf compiler-related utilities.
|
||||
- [🔨 Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) - Go utilities and helper functions.
|
||||
- [🛠 Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) - Makefile scripts for automation.
|
||||
- [📜 Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) - Utility scripts for development.
|
||||
|
||||
## 📋 Standards and Conventions
|
||||
- [🚦 Commit Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) - Standards for writing commit messages.
|
||||
- [✅ Testing Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) - Guidelines and conventions for writing tests.
|
||||
- [📈 Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) - Version management for the project.
|
||||
|
||||
## 🖼 Additional Resources
|
||||
- [🌐 API Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) - Detailed API documentation.
|
||||
- [📚 Go Code Standards](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) - Go programming language standards.
|
||||
- [🖼 Image Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) - Guidelines for image assets.
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
- [🔍 Error Code Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) - List of error codes and their meanings.
|
||||
- [🐚 Bash Logging](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) - Logging standards for bash scripts.
|
||||
- [📈 Logging Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) - Conventions for application logging.
|
||||
- [🛠 Local Actions Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) - How to perform local actions for troubleshooting.
|
||||
@ -24,7 +24,48 @@
|
||||
- Do not use uppercase characters, underscores, or dashes in package names.
|
||||
- Please consider parent directory name when choosing a package name. For example, `pkg/controllers/autoscaler/foo.go` should say `package autoscaler` not `package autoscalercontroller`.
|
||||
- Unless there's a good reason, the `package foo` line should match the name of the directory in which the `.go` file exists.
|
||||
- Importers can use a different name if they need to disambiguate.
|
||||
- Importers can use a different name if they need to disambiguate.Ⓜ️
|
||||
|
||||
## OpenIM Naming Conventions Guide
|
||||
|
||||
Welcome to the OpenIM Naming Conventions Guide. This document outlines the best practices and standardized naming conventions that our project follows to maintain clarity, consistency, and alignment with industry standards, specifically taking cues from the Google Naming Conventions.
|
||||
|
||||
### 1. General File Naming
|
||||
|
||||
Files within the OpenIM project should adhere to the following rules:
|
||||
|
||||
+ Both hyphens (`-`) and underscores (`_`) are acceptable in file names.
|
||||
+ Underscores (`_`) are preferred for general files to enhance readability and compatibility.
|
||||
+ For example: `data_processor.py`, `user_profile_generator.go`
|
||||
|
||||
### 2. Special File Types
|
||||
|
||||
#### a. Script and Markdown Files
|
||||
|
||||
+ Bash scripts and Markdown files should use hyphens (`-`) to facilitate better searchability and compatibility in web browsers.
|
||||
+ For example: `deploy-script.sh`, `project-overview.md`
|
||||
|
||||
#### b. Uppercase Markdown Documentation
|
||||
|
||||
+ Markdown files with uppercase names, such as `README`, may include underscores (`_`) to separate words if necessary.
|
||||
+ For example: `README_SETUP.md`, `CONTRIBUTING_GUIDELINES.md`
|
||||
|
||||
### 3. Directory Naming
|
||||
|
||||
+ Directories must use hyphens (`-`) exclusively to maintain a clean and organized file structure.
|
||||
+ For example: `image-assets`, `user-data`
|
||||
|
||||
### 4. Configuration Files
|
||||
|
||||
+ Configuration files, including but not limited to `.yaml` files, should use hyphens (`-`).
|
||||
+ For example: `app-config.yaml`, `logging-config.yaml`
|
||||
|
||||
### Best Practices
|
||||
|
||||
+ Keep names concise but descriptive enough to convey the file's purpose or contents at a glance.
|
||||
+ Avoid using spaces in names; use hyphens or underscores instead to improve compatibility across different operating systems and environments.
|
||||
+ Stick to lowercase naming where possible for consistency and to prevent issues with case-sensitive systems.
|
||||
+ Include version numbers or dates in file names if the file is subject to updates, following the format: `project-plan-v1.2.md` or `backup-2023-03-15.sql`.
|
||||
|
||||
## Directory and file conventions
|
||||
|
||||
@ -136,13 +136,13 @@ For convenience, configuration through modifying environment variables is recomm
|
||||
export PASSWORD="openIM123"
|
||||
```
|
||||
|
||||
+ USER
|
||||
+ OPENIM_USER
|
||||
|
||||
+ **Description**: Username for mysql, mongodb, redis, and minio.
|
||||
+ **Default**: `root`
|
||||
|
||||
```bash
|
||||
export USER="root"
|
||||
export OPENIM_USER="root"
|
||||
```
|
||||
|
||||
+ API_URL
|
||||
|
||||
@ -83,7 +83,7 @@ You may now edit files on the `myfeature` branch.
|
||||
|
||||
### Building open-im-server
|
||||
|
||||
This workflow is process-specific. For quick-start build instructions for [openimsdk/open-im-server](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util_makefile.md)
|
||||
This workflow is process-specific. For quick-start build instructions for [openimsdk/open-im-server](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md)
|
||||
|
||||
## 4. Keep your branch in sync
|
||||
|
||||
@ -312,6 +312,18 @@ The naming convention is a very important part of the code specification. A unif
|
||||
- The function name is in camel case, and the first letter is uppercase or lowercase according to the access control decision,For example: `MixedCaps` or `mixedCaps`.
|
||||
- Code automatically generated by code generation tools (such as `xxxx.pb.go`) and underscores used to group related test cases (such as `TestMyFunction_WhatIsBeingTested`) exclude this rule.
|
||||
|
||||
In accordance with the naming conventions adopted by OpenIM and drawing reference from the Google Naming Conventions as per the guidelines available at https://google.github.io/styleguide/go/, the following expectations for naming practices within the project are set forth:
|
||||
|
||||
1. **File Names:**
|
||||
+ Both hyphens (`-`) and underscores (`_`) are permitted when naming files.
|
||||
+ A preference is established for the use of underscores (`_`), suggesting it as the best practice in general scenarios.
|
||||
2. **Script and Markdown Files:**
|
||||
+ For shell scripts (bash files) and Markdown (`.md`) documents, the use of hyphens (`-`) is recommended.
|
||||
+ This recommendation is based on the improved searchability and compatibility in web browsers when hyphens are used in names.
|
||||
3. **Directories:**
|
||||
+ A consistent approach is mandated for naming directories, exclusively using hyphens (`-`) to separate words within directory names.
|
||||
|
||||
|
||||
### 2.3 File Naming
|
||||
|
||||
- Keep the filename short and meaningful.
|
||||
@ -26,11 +26,11 @@ docker pull minio/minio
|
||||
|
||||
## 2. OpenIM & Chat Images
|
||||
|
||||
**For detailed understanding of version management and storage of OpenIM and Chat**: [version.md](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/version.md)
|
||||
**For detailed understanding of version management and storage of OpenIM and Chat**: [version.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md)
|
||||
|
||||
### OpenIM Image
|
||||
|
||||
- Get image version info: [images.md](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md)
|
||||
- Get image version info: [images.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
|
||||
- Depending on the required version, execute the following command:
|
||||
|
||||
```bash
|
||||
|
||||
183
docs/contrib/test.md
Normal file
183
docs/contrib/test.md
Normal file
@ -0,0 +1,183 @@
|
||||
# OpenIM RPC Service Test Control Script Documentation
|
||||
|
||||
This document serves as a comprehensive guide to understanding and utilizing the `test.sh` script for testing OpenIM RPC services. The `test.sh` script is a collection of bash functions designed to test various aspects of the OpenIM RPC services, ensuring that each part of the API is functioning as expected.
|
||||
|
||||
+ Scripts:https://github.com/OpenIMSDK/Open-IM-Server/tree/main/scripts/install/test.sh
|
||||
|
||||
For some complex, bulky functional tests, performance tests, and various e2e tests, We are all in the current warehouse to https://github.com/OpenIMSDK/Open-IM-Server/tree/main/test or https://github.com/openim-sigs/test-infra directory In the.
|
||||
|
||||
+ About OpenIM Feature [Test Docs](https://docs.google.com/spreadsheets/d/1zELWkwxgOOZ7u5pmYCqqaFnvZy2SVajv/edit?usp=sharing&ouid=103266350914914783293&rtpof=true&sd=true)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
The `test.sh` script is located within the `./scripts/install/` directory of the OpenIM service's codebase. To use the script, navigate to this directory from your terminal:
|
||||
|
||||
```bash
|
||||
cd ./scripts/install/
|
||||
chmod +x test.sh
|
||||
```
|
||||
|
||||
### Running the Entire Test Suite
|
||||
|
||||
To execute all available tests, you can either call the script directly or use the `make` command:
|
||||
|
||||
```
|
||||
./test.sh openim::test::test
|
||||
```
|
||||
|
||||
Or, if you have a `Makefile` that defines the `test-api` target:
|
||||
|
||||
```bash
|
||||
make test-api
|
||||
```
|
||||
|
||||
Alternatively, you can invoke specific test functions by passing them as arguments:
|
||||
|
||||
```
|
||||
./test.sh openim::test::<function_name>
|
||||
```
|
||||
|
||||
This `make` command should be equivalent to running `./test.sh openim::test::test`, provided that the `Makefile` is configured accordingly.
|
||||
|
||||
|
||||
|
||||
### Executing Individual Test Functions
|
||||
|
||||
If you wish to run a specific set of tests, you can call the relevant function by passing it as an argument to the script. Here are some examples:
|
||||
|
||||
**Message Tests:**
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::msg
|
||||
```
|
||||
|
||||
**Authentication Tests:**
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::auth
|
||||
```
|
||||
|
||||
**User Tests:**
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::user
|
||||
```
|
||||
|
||||
**Friend Tests:**
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::friend
|
||||
```
|
||||
|
||||
**Group Tests:**
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::group
|
||||
```
|
||||
|
||||
Each of these commands will run the test suite associated with the specific functionality of the OpenIM service.
|
||||
|
||||
|
||||
|
||||
### Detailed Function Test Examples
|
||||
|
||||
T**esting Message Sending and Receiving:**
|
||||
|
||||
To test message functionality, the `openim::test::msg` function is called. It will register a user, send a message, and clear messages to ensure that the messaging service is operational.
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::msg
|
||||
```
|
||||
|
||||
**Testing User Registration and Account Checks:**
|
||||
|
||||
The `openim::test::user` function will create new user accounts and perform a series of checks on these accounts to verify that user registration and account queries are functioning properly.
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::user
|
||||
```
|
||||
|
||||
**Testing Friend Management:**
|
||||
|
||||
By invoking `openim::test::friend`, the script will test adding friends, checking friendship status, managing friend requests, and handling blacklisting.
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::friend
|
||||
```
|
||||
|
||||
**Testing Group Operations:**
|
||||
|
||||
The `openim::test::group` function tests group creation, member addition, information retrieval, and member management within groups.
|
||||
|
||||
```bash
|
||||
./test.sh openim::test::group
|
||||
```
|
||||
|
||||
### Log Output
|
||||
|
||||
Each test function will output logs to the terminal to confirm the success or failure of the tests. These logs are crucial for identifying issues and verifying that each part of the service is tested thoroughly.
|
||||
|
||||
Each function logs its success upon completion, which aids in debugging and understanding the test flow. The success message is standardized across functions:
|
||||
|
||||
```
|
||||
openim::log::success "<Test suite name> completed successfully."
|
||||
```
|
||||
|
||||
By following the guidelines and instructions outlined in this document, you can effectively utilize the `test.sh` script to test and verify the OpenIM RPC services' functionality.
|
||||
|
||||
|
||||
|
||||
## Function feature
|
||||
|
||||
| Function Name | Corresponding API/Action | Function Purpose |
|
||||
| ---------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `openim::test::msg` | Messaging Operations | Tests all aspects of messaging, including sending, receiving, and managing messages. |
|
||||
| `openim::test::auth` | Authentication Operations | Validates the authentication process and session management, including token handling and forced logout. |
|
||||
| `openim::test::user` | User Account Operations | Covers testing for user account creation, retrieval, updating, and overall management. |
|
||||
| `openim::test::friend` | Friend Relationship Operations | Ensures friend management functions correctly, including requests, listing, and blacklisting. |
|
||||
| `openim::test::group` | Group Management Operations | Checks group-related functionalities like creation, invitation, information retrieval, and member management. |
|
||||
| `openim::test::send_msg` | Send Message API | Simulates sending a message from one user to another or within a group. |
|
||||
| `openim::test::revoke_msg` | Revoke Message API (TODO) | (Planned) Will test the revocation of a previously sent message. |
|
||||
| `openim::test::user_register` | User Registration API | Registers a new user in the system to validate the registration process. |
|
||||
| `openim::test::check_account` | Account Check API | Checks if an account exists for a given user ID. |
|
||||
| `openim::test::user_clear_all_msg` | Clear All Messages API | Clears all messages for a given user to validate message history management. |
|
||||
| `openim::test::get_token` | Token Retrieval API | Retrieves an authentication token to validate token management. |
|
||||
| `openim::test::force_logout` | Force Logout API | Forces a logout for a test user to validate session control. |
|
||||
| `openim::test::check_user_account` | User Account Existence Check API | Confirms the existence of a test user's account. |
|
||||
| `openim::test::get_users` | Get Users API | Retrieves a list of users to validate user query functionality. |
|
||||
| `openim::test::get_users_info` | Get User Information API | Obtains detailed information for a given user. |
|
||||
| `openim::test::get_users_online_status` | Get User Online Status API | Checks the online status of a user to validate presence functionality. |
|
||||
| `openim::test::update_user_info` | Update User Information API | Updates a user's information to validate account update capabilities. |
|
||||
| `openim::test::get_subscribe_users_status` | Get Subscribed Users' Status API | Retrieves the status of users that a test user has subscribed to. |
|
||||
| `openim::test::subscribe_users_status` | Subscribe to Users' Status API | Subscribes a test user to a set of user statuses. |
|
||||
| `openim::test::set_global_msg_recv_opt` | Set Global Message Receiving Option API | Sets the message receiving option for a test user. |
|
||||
| `openim::test::is_friend` | Check Friendship Status API | Verifies if two users are friends within the system. |
|
||||
| `openim::test::add_friend` | Send Friend Request API | Sends a friend request from one user to another. |
|
||||
| `openim::test::get_friend_list` | Get Friend List API | Retrieves the friend list of a test user. |
|
||||
| `openim::test::get_friend_apply_list` | Get Friend Application List API | Retrieves friend applications for a test user. |
|
||||
| `openim::test::get_self_friend_apply_list` | Get Self-Friend Application List API | Retrieves the friend applications that the user has applied for. |
|
||||
| `openim::test::add_black` | Add User to Blacklist API | Adds a user to the test user's blacklist to validate blacklist functionality. |
|
||||
| `openim::test::remove_black` | Remove User from Blacklist API | Removes a user from the test user's blacklist. |
|
||||
| `openim::test::get_black_list` | Get Blacklist API | Retrieves the blacklist for a test user. |
|
||||
| `openim::test::create_group` | Group Creation API | Creates a new group with test users to validate group creation. |
|
||||
| `openim::test::invite_user_to_group` | Invite User to Group API | Invites a user to join a group to test invitation functionality. |
|
||||
| `openim::test::transfer_group` | Group Ownership Transfer API | Tests the transfer of group ownership from one member to another. |
|
||||
| `openim::test::get_groups_info` | Get Group Information API | Retrieves information for specified groups to validate group query functionality. |
|
||||
| `openim::test::kick_group` | Kick User from Group API | Simulates kicking a user from a group to test group membership management. |
|
||||
| `openim::test::get_group_members_info` | Get Group Members Information API | Obtains detailed information for members of a specified group. |
|
||||
| `openim::test::get_group_member_list` | Get Group Member List API | Retrieves a list of members for a given group to ensure member listing is functional. |
|
||||
| `openim::test::get_joined_group_list` | Get Joined Group List API | Retrieves a list of groups that a user has joined to validate user's group memberships. |
|
||||
| `openim::test::set_group_member_info` | Set Group Member Information API | Updates the information for a group member to test the update functionality. |
|
||||
| `openim::test::mute_group` | Mute Group API | Tests the ability to mute a group, disabling message notifications for its members. |
|
||||
| `openim::test::cancel_mute_group` | Cancel Mute Group API | Tests the ability to cancel the mute status of a group, re-enabling message notifications. |
|
||||
| `openim::test::dismiss_group` | Dismiss Group API | Tests the ability to dismiss and delete a group from the system. |
|
||||
| `openim::test::cancel_mute_group_member` | Cancel Mute Group Member API | Tests the ability to cancel mute status for a specific group member. |
|
||||
| `openim::test::join_group` | Join Group API (TODO) | (Planned) Will test the functionality for a user to join a specified group. |
|
||||
| `openim::test::set_group_info` | Set Group Information API | Tests the ability to update the group information, such as the name or description. |
|
||||
| `openim::test::quit_group` | Quit Group API | Tests the functionality for a user to leave a specified group. |
|
||||
| `openim::test::get_recv_group_applicationList` | Get Received Group Application List API | Retrieves the list of group applications received by a user to validate application management. |
|
||||
| `openim::test::group_application_response` | Group Application Response API (TODO) | (Planned) Will test the functionality to respond to a group join request. |
|
||||
| `openim::test::get_user_req_group_applicationList` | Get User Requested Group Application List API | Retrieves the list of group applications requested by a user to validate tracking of user's applications. |
|
||||
| `openim::test::mute_group_member` | Mute Group Member API | Tests the ability to mute a specific member within a group, disabling their ability to send messages. |
|
||||
| `openim::test::get_group_users_req_application_list` | Get Group Users Request Application List API | Retrieves a list of user requests for group applications to validate group request management. |
|
||||
@ -212,4 +212,4 @@ Throughout this process, active communication within the team is pivotal to main
|
||||
|
||||
## Docker Images Version Management
|
||||
|
||||
For more details on managing Docker image versions, visit [OpenIM Docker Images Administration](https://github.com/openimsdk/open-im-server/blob/main/docs/conversions/images.md).
|
||||
For more details on managing Docker image versions, visit [OpenIM Docker Images Administration](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md).
|
||||
@ -1,10 +0,0 @@
|
||||
## OpenIM Project Development Standards
|
||||
|
||||
- [Code Standards](./go_code.md)
|
||||
- [Docker Images Standards](./images.md)
|
||||
- [Directory Standards](./directory.md)
|
||||
- [Commit Standards](./commit.md)
|
||||
- [Versioning Standards](./version.md)
|
||||
- [Interface Standards](./api.md)
|
||||
- [Log Standards](./log.md)
|
||||
- [Error Code Standards](./error_code.md)
|
||||
4
go.mod
4
go.mod
@ -45,6 +45,7 @@ require (
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
@ -73,6 +74,7 @@ require (
|
||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
|
||||
@ -116,6 +118,7 @@ require (
|
||||
github.com/sergi/go-diff v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
@ -124,6 +127,7 @@ require (
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
go.opencensus.io v0.24.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
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@ -144,6 +144,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
@ -311,6 +312,7 @@ github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jW
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@ -350,6 +352,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
@ -531,6 +535,8 @@ gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb
|
||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
15
install.sh
15
install.sh
@ -458,25 +458,20 @@ function openim_color() {
|
||||
}
|
||||
|
||||
# --- helper functions for logs ---
|
||||
info()
|
||||
{
|
||||
info() {
|
||||
echo -e "[${GREEN_PREFIX}INFO${COLOR_SUFFIX}] " "$@"
|
||||
}
|
||||
warn()
|
||||
{
|
||||
warn() {
|
||||
echo -e "[${YELLOW_PREFIX}WARN${COLOR_SUFFIX}] " "$@" >&2
|
||||
}
|
||||
fatal()
|
||||
{
|
||||
fatal() {
|
||||
echo -e "[${RED_PREFIX}ERROR${COLOR_SUFFIX}] " "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
debug()
|
||||
{
|
||||
debug() {
|
||||
echo -e "[${BLUE_PREFIX}DEBUG${COLOR_SUFFIX}]===> " "$@"
|
||||
}
|
||||
success()
|
||||
{
|
||||
success() {
|
||||
echo -e "${BRIGHT_GREEN_PREFIX}=== [SUCCESS] ===${COLOR_SUFFIX}\n=> " "$@"
|
||||
}
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ EOF
|
||||
|
||||
cd scripts;
|
||||
chmod +x *.sh;
|
||||
./init_pwd.sh;
|
||||
./init-pwd.sh;
|
||||
./env_check.sh;
|
||||
cd ..;
|
||||
docker-compose up -d;
|
||||
|
||||
@ -167,7 +167,7 @@ func (c *Client) readMessage() {
|
||||
func (c *Client) handleMessage(message []byte) error {
|
||||
if c.IsCompress {
|
||||
var err error
|
||||
message, err = c.longConnServer.DeCompress(message)
|
||||
message, err = c.longConnServer.DecompressWithPool(message)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
@ -317,7 +317,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error {
|
||||
|
||||
_ = c.conn.SetWriteDeadline(writeWait)
|
||||
if c.IsCompress {
|
||||
resultBuf, compressErr := c.longConnServer.Compress(encodedBuf)
|
||||
resultBuf, compressErr := c.longConnServer.CompressWithPool(encodedBuf)
|
||||
if compressErr != nil {
|
||||
return utils.Wrap(compressErr, "")
|
||||
}
|
||||
|
||||
@ -17,14 +17,23 @@ package msggateway
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
gzipWriterPool = sync.Pool{New: func() any { return gzip.NewWriter(nil) }}
|
||||
gzipReaderPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
|
||||
)
|
||||
|
||||
type Compressor interface {
|
||||
Compress(rawData []byte) ([]byte, error)
|
||||
CompressWithPool(rawData []byte) ([]byte, error)
|
||||
DeCompress(compressedData []byte) ([]byte, error)
|
||||
DecompressWithPool(compressedData []byte) ([]byte, error)
|
||||
}
|
||||
type GzipCompressor struct {
|
||||
compressProtocol string
|
||||
@ -46,6 +55,22 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
|
||||
return gzipBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) {
|
||||
gz := gzipWriterPool.Get().(*gzip.Writer)
|
||||
defer gzipWriterPool.Put(gz)
|
||||
|
||||
gzipBuffer := bytes.Buffer{}
|
||||
gz.Reset(&gzipBuffer)
|
||||
|
||||
if _, err := gz.Write(rawData); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
return gzipBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
|
||||
buff := bytes.NewBuffer(compressedData)
|
||||
reader, err := gzip.NewReader(buff)
|
||||
@ -59,3 +84,23 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
|
||||
_ = reader.Close()
|
||||
return compressedData, nil
|
||||
}
|
||||
|
||||
func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, error) {
|
||||
reader := gzipReaderPool.Get().(*gzip.Reader)
|
||||
if reader == nil {
|
||||
return nil, errors.New("NewReader failed")
|
||||
}
|
||||
defer gzipReaderPool.Put(reader)
|
||||
|
||||
err := reader.Reset(bytes.NewReader(compressedData))
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "NewReader failed")
|
||||
}
|
||||
|
||||
compressedData, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "ReadAll failed")
|
||||
}
|
||||
_ = reader.Close()
|
||||
return compressedData, nil
|
||||
}
|
||||
|
||||
107
internal/msggateway/compressor_test.go
Normal file
107
internal/msggateway/compressor_test.go
Normal file
@ -0,0 +1,107 @@
|
||||
package msggateway
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockRandom() []byte {
|
||||
bs := make([]byte, 50)
|
||||
rand.Read(bs)
|
||||
return bs
|
||||
}
|
||||
|
||||
func TestCompressDecompress(t *testing.T) {
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
src := mockRandom()
|
||||
|
||||
// compress
|
||||
dest, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// decompress
|
||||
res, err := compressor.DecompressWithPool(dest)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// check
|
||||
assert.EqualValues(t, src, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressDecompressWithConcurrency(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
src := mockRandom()
|
||||
|
||||
// compress
|
||||
dest, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// decompress
|
||||
res, err := compressor.DecompressWithPool(dest)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
// check
|
||||
assert.EqualValues(t, src, res)
|
||||
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkCompress(b *testing.B) {
|
||||
src := mockRandom()
|
||||
compressor := NewGzipCompressor()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompressWithSyncPool(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.CompressWithPool(src)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecompress(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
comdata, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.DeCompress(comdata)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecompressWithSyncPool(b *testing.B) {
|
||||
src := mockRandom()
|
||||
|
||||
compressor := NewGzipCompressor()
|
||||
comdata, err := compressor.Compress(src)
|
||||
assert.Equal(b, nil, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := compressor.DecompressWithPool(comdata)
|
||||
assert.Equal(b, nil, err)
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
// RunWsAndServer run ws server
|
||||
// RunWsAndServer run ws server.
|
||||
func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error {
|
||||
fmt.Println(
|
||||
"start rpc/msg_gateway server, port: ",
|
||||
|
||||
@ -17,32 +17,28 @@ package msggateway
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
type LongConnServer interface {
|
||||
@ -78,7 +74,6 @@ type WsServer struct {
|
||||
onlineUserNum atomic.Int64
|
||||
onlineUserConnNum atomic.Int64
|
||||
handshakeTimeout time.Duration
|
||||
hubServer *Server
|
||||
validate *validator.Validate
|
||||
cache cache.MsgModel
|
||||
userClient *rpcclient.UserRpcClient
|
||||
@ -183,27 +178,39 @@ func (ws *WsServer) Run() error {
|
||||
return http.ListenAndServe(":"+utils.IntToString(ws.port), nil) // Start listening
|
||||
}
|
||||
|
||||
var concurrentRequest = 3
|
||||
|
||||
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
|
||||
conns, err := ws.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentRequest)
|
||||
|
||||
// Online push user online message to other node
|
||||
for _, v := range conns {
|
||||
v := v // safe closure var
|
||||
if v.Target() == ws.disCov.GetSelfConnTarget() {
|
||||
log.ZDebug(ctx, "Filter out this node", "node", v.Target())
|
||||
continue
|
||||
}
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
_, err := msgClient.MultiTerminalLoginCheck(ctx, &msggateway.MultiTerminalLoginCheckReq{
|
||||
UserID: client.UserID,
|
||||
PlatformID: int32(client.PlatformID), Token: client.token,
|
||||
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
_, err := msgClient.MultiTerminalLoginCheck(ctx, &msggateway.MultiTerminalLoginCheckReq{
|
||||
UserID: client.UserID,
|
||||
PlatformID: int32(client.PlatformID), Token: client.token,
|
||||
})
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -221,7 +228,7 @@ func (ws *WsServer) registerClient(client *Client) {
|
||||
if !userOK {
|
||||
ws.clients.Set(client.UserID, client)
|
||||
log.ZDebug(client.ctx, "user not exist", "userID", client.UserID, "platformID", client.PlatformID)
|
||||
prom_metrics.OnlineUserGauge.Add(1)
|
||||
prommetrics.OnlineUserGauge.Add(1)
|
||||
ws.onlineUserNum.Add(1)
|
||||
ws.onlineUserConnNum.Add(1)
|
||||
} else {
|
||||
@ -289,70 +296,72 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
|
||||
}
|
||||
fallthrough
|
||||
case constant.AllLoginButSameTermKick:
|
||||
if clientOK {
|
||||
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
|
||||
if isDeleteUser {
|
||||
ws.onlineUserNum.Add(-1)
|
||||
if !clientOK {
|
||||
return
|
||||
}
|
||||
|
||||
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
|
||||
if isDeleteUser {
|
||||
ws.onlineUserNum.Add(-1)
|
||||
}
|
||||
for _, c := range oldClients {
|
||||
err := c.KickOnlineMessage()
|
||||
if err != nil {
|
||||
log.ZWarn(c.ctx, "KickOnlineMessage", err)
|
||||
}
|
||||
for _, c := range oldClients {
|
||||
err := c.KickOnlineMessage()
|
||||
if err != nil {
|
||||
log.ZWarn(c.ctx, "KickOnlineMessage", err)
|
||||
}
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(
|
||||
newClient.ctx,
|
||||
newClient.UserID,
|
||||
newClient.PlatformID,
|
||||
)
|
||||
if err != nil && err != redis.Nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
newClient.UserID,
|
||||
newClient.PlatformID,
|
||||
)
|
||||
if err != nil && err != redis.Nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"get token from redis err",
|
||||
err,
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"m is nil",
|
||||
errors.New("m is nil"),
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.ZDebug(
|
||||
newClient.ctx,
|
||||
"get token from redis",
|
||||
"get token from redis err",
|
||||
err,
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
"tokenMap",
|
||||
m,
|
||||
)
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"m is nil",
|
||||
errors.New("m is nil"),
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.ZDebug(
|
||||
newClient.ctx,
|
||||
"get token from redis",
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
"tokenMap",
|
||||
m,
|
||||
)
|
||||
|
||||
for k := range m {
|
||||
if k != newClient.ctx.GetToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
|
||||
newClient.UserID, "token", newClient.ctx.GetToken())
|
||||
err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
|
||||
if err != nil {
|
||||
log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
|
||||
return
|
||||
for k := range m {
|
||||
if k != newClient.ctx.GetToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
|
||||
newClient.UserID, "token", newClient.ctx.GetToken())
|
||||
err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
|
||||
if err != nil {
|
||||
log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,7 +370,7 @@ func (ws *WsServer) unregisterClient(client *Client) {
|
||||
isDeleteUser := ws.clients.delete(client.UserID, client.ctx.GetRemoteAddr())
|
||||
if isDeleteUser {
|
||||
ws.onlineUserNum.Add(-1)
|
||||
prom_metrics.OnlineUserGauge.Dec()
|
||||
prommetrics.OnlineUserGauge.Dec()
|
||||
}
|
||||
ws.onlineUserConnNum.Add(-1)
|
||||
ws.SetUserOnlineStatus(client.ctx, client, constant.Offline)
|
||||
@ -404,7 +413,7 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpError(connContext, errs.ErrConnArgsErr)
|
||||
return
|
||||
}
|
||||
if err := authverify.WsVerifyToken(token, userID, platformID); err != nil {
|
||||
if err = authverify.WsVerifyToken(token, userID, platformID); err != nil {
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -17,18 +17,16 @@ package msgtransfer
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/discovery_register"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
@ -36,6 +34,8 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/relation"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
@ -65,7 +65,7 @@ func StartTransfer(prometheusPort int) error {
|
||||
if err := mongo.CreateMsgIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := discovery_register.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
/*
|
||||
client, err := openkeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema,
|
||||
openkeeper.WithFreq(time.Hour), openkeeper.WithRoundRobin(), openkeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username,
|
||||
@ -123,7 +123,7 @@ func (m *MsgTransfer) Start(prometheusPort int) error {
|
||||
reg.MustRegister(
|
||||
collectors.NewGoCollector(),
|
||||
)
|
||||
reg.MustRegister(prom_metrics.GetGrpcCusMetrics("Transfer")...)
|
||||
reg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...)
|
||||
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil))
|
||||
}
|
||||
|
||||
@ -427,49 +427,62 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
|
||||
break
|
||||
}
|
||||
}
|
||||
rwLock := new(sync.RWMutex)
|
||||
log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset",
|
||||
claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition())
|
||||
cMsg := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
t := time.NewTicker(time.Millisecond * 100)
|
||||
|
||||
split := 1000
|
||||
rwLock := new(sync.RWMutex)
|
||||
messages := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if len(cMsg) > 0 {
|
||||
rwLock.Lock()
|
||||
ccMsg := make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
for _, v := range cMsg {
|
||||
ccMsg = append(ccMsg, v)
|
||||
}
|
||||
cMsg = make([]*sarama.ConsumerMessage, 0, 1000)
|
||||
rwLock.Unlock()
|
||||
split := 1000
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
|
||||
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(ccMsg))
|
||||
for i := 0; i < len(ccMsg)/split; i++ {
|
||||
// log.Debug()
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: ccMsg[i*split : (i+1)*split],
|
||||
}}
|
||||
}
|
||||
if (len(ccMsg) % split) > 0 {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: ccMsg[split*(len(ccMsg)/split):],
|
||||
}}
|
||||
}
|
||||
log.ZDebug(ctx, "timer trigger msg consumer end", "length", len(ccMsg))
|
||||
case <-ticker.C:
|
||||
if len(messages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rwLock.Lock()
|
||||
buffer := make([]*sarama.ConsumerMessage, 0, len(messages))
|
||||
buffer = append(buffer, messages...)
|
||||
|
||||
// reuse slice, set cap to 0
|
||||
messages = messages[:0]
|
||||
rwLock.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
|
||||
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(buffer))
|
||||
for i := 0; i < len(buffer)/split; i++ {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: buffer[i*split : (i+1)*split],
|
||||
}}
|
||||
}
|
||||
if (len(buffer) % split) > 0 {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
ctx: ctx, cMsgList: buffer[split*(len(buffer)/split):],
|
||||
}}
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "timer trigger msg consumer end",
|
||||
"length", len(buffer), "time_cost", time.Since(start),
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for msg := range claim.Messages() {
|
||||
rwLock.Lock()
|
||||
if len(msg.Value) != 0 {
|
||||
cMsg = append(cMsg, msg)
|
||||
if len(msg.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rwLock.Lock()
|
||||
messages = append(messages, msg)
|
||||
rwLock.Unlock()
|
||||
|
||||
sess.MarkMessage(msg, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -16,7 +16,6 @@ package msgtransfer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@ -27,6 +26,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
)
|
||||
|
||||
type OnlineHistoryMongoConsumerHandler struct {
|
||||
@ -75,9 +75,9 @@ func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(
|
||||
"conversationID",
|
||||
msgFromMQ.ConversationID,
|
||||
)
|
||||
prom_metrics.MsgInsertMongoFailedCounter.Inc()
|
||||
prommetrics.MsgInsertMongoFailedCounter.Inc()
|
||||
} else {
|
||||
prom_metrics.MsgInsertMongoSuccessCounter.Inc()
|
||||
prommetrics.MsgInsertMongoSuccessCounter.Inc()
|
||||
}
|
||||
var seqs []int64
|
||||
for _, msg := range msgFromMQ.MsgData {
|
||||
|
||||
@ -1,32 +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.
|
||||
|
||||
package fcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
|
||||
func Test_Push(t *testing.T) {
|
||||
var redis cache.MsgModel
|
||||
offlinePusher := NewClient(redis)
|
||||
err := offlinePusher.Push(context.Background(), []string{"userID1"}, "test", "test", &offlinepush.Opts{})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@ -18,14 +18,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
@ -34,6 +31,7 @@ import (
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
|
||||
@ -41,6 +39,7 @@ import (
|
||||
"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/localcache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
@ -53,7 +52,6 @@ type Pusher struct {
|
||||
msgRpcClient *rpcclient.MessageRpcClient
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
successCount int
|
||||
}
|
||||
|
||||
var errNoOfflinePusher = errors.New("no offlinePusher is configured")
|
||||
@ -104,24 +102,29 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg
|
||||
if err := callbackOnlinePush(ctx, userIDs, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// push
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs)
|
||||
p.successCount++
|
||||
if isOfflinePush {
|
||||
for _, v := range wsResults {
|
||||
if msg.SendID != v.UserID && (!v.OnlinePush) {
|
||||
if err := callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePushMsg(ctx, msg.SendID, msg, []string{v.UserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isOfflinePush {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range wsResults {
|
||||
if msg.SendID != v.UserID && (!v.OnlinePush) {
|
||||
if err = callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.offlinePushMsg(ctx, msg.SendID, msg, []string{v.UserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,14 +143,16 @@ func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t interface{}) error {
|
||||
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err := callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil {
|
||||
if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pushToUserIDs) == 0 {
|
||||
pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
@ -155,7 +160,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
|
||||
return err
|
||||
}
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, []string{tips.QuitUser.UserID})
|
||||
@ -167,7 +172,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
|
||||
}
|
||||
kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, kickedUsers)
|
||||
@ -183,48 +188,61 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err := p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
|
||||
p.successCount++
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if isOfflinePush {
|
||||
var onlineSuccessUserIDs []string
|
||||
var WebAndPcBackgroundUserIDs []string
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, msg.SendID)
|
||||
var (
|
||||
onlineSuccessUserIDs = []string{msg.SendID}
|
||||
webAndPcBackgroundUserIDs []string
|
||||
)
|
||||
|
||||
for _, v := range wsResults {
|
||||
if v.OnlinePush && v.UserID != msg.SendID {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
if !v.OnlinePush {
|
||||
if len(v.Resp) != 0 {
|
||||
for _, singleResult := range v.Resp {
|
||||
if singleResult.ResultCode == -2 {
|
||||
if constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC ||
|
||||
singleResult.RecvPlatFormID == constant.WebPlatformID {
|
||||
WebAndPcBackgroundUserIDs = append(WebAndPcBackgroundUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v.OnlinePush {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(v.Resp) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, singleResult := range v.Resp {
|
||||
if singleResult.ResultCode != -2 {
|
||||
continue
|
||||
}
|
||||
|
||||
isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC
|
||||
isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID
|
||||
|
||||
if isPC || isWebID {
|
||||
webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
notNotificationUserIDs, err := p.conversationLocalCache.GetRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
// log.ZError(ctx, "GetRecvMsgNotNotifyUserIDs failed", err, "groupID", groupID)
|
||||
return err
|
||||
}
|
||||
|
||||
needOfflinePushUserIDs = utils.SliceSub(needOfflinePushUserIDs, notNotificationUserIDs)
|
||||
}
|
||||
// Use offline push messaging
|
||||
@ -234,6 +252,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
@ -250,8 +269,8 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, WebAndPcBackgroundUserIDs)); err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs))
|
||||
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -288,7 +307,7 @@ func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg
|
||||
}
|
||||
err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prom_metrics.MsgOfflinePushFailedCounter.Inc()
|
||||
prommetrics.MsgOfflinePushFailedCounter.Inc()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -319,15 +338,18 @@ func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData)
|
||||
err = errNoOfflinePusher
|
||||
return
|
||||
}
|
||||
type AtContent struct {
|
||||
|
||||
type atContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
opts, err = p.GetOfflinePushOpts(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
@ -345,9 +367,9 @@ func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData)
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
a := AtContent{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &a)
|
||||
if utils.IsContain(conversationID, a.AtUserList) {
|
||||
ac := atContent{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &ac)
|
||||
if utils.IsContain(conversationID, ac.AtUserList) {
|
||||
title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
} else {
|
||||
title = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
|
||||
@ -16,7 +16,6 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
|
||||
@ -35,6 +34,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
@ -74,7 +74,7 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prom_metrics.UserLoginCounter.Inc()
|
||||
prommetrics.UserLoginCounter.Inc()
|
||||
resp.Token = token
|
||||
resp.ExpireTimeSeconds = config.Config.TokenPolicy.Expire * 24 * 60 * 60
|
||||
return &resp, nil
|
||||
|
||||
@ -16,8 +16,8 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prom_metrics"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
@ -59,7 +59,7 @@ func (m *msgServer) sendMsgSuperGroupChat(
|
||||
req *pbmsg.SendMsgReq,
|
||||
) (resp *pbmsg.SendMsgResp, err error) {
|
||||
if err = m.messageVerification(ctx, req); err != nil {
|
||||
prom_metrics.GroupChatMsgProcessFailedCounter.Inc()
|
||||
prommetrics.GroupChatMsgProcessFailedCounter.Inc()
|
||||
return nil, err
|
||||
}
|
||||
if err = callbackBeforeSendGroupMsg(ctx, req); err != nil {
|
||||
@ -78,7 +78,7 @@ func (m *msgServer) sendMsgSuperGroupChat(
|
||||
if err = callbackAfterSendGroupMsg(ctx, req); err != nil {
|
||||
log.ZWarn(ctx, "CallbackAfterSendGroupMsg", err)
|
||||
}
|
||||
prom_metrics.GroupChatMsgProcessSuccessCounter.Inc()
|
||||
prommetrics.GroupChatMsgProcessSuccessCounter.Inc()
|
||||
resp = &pbmsg.SendMsgResp{}
|
||||
resp.SendTime = req.MsgData.SendTime
|
||||
resp.ServerMsgID = req.MsgData.ServerMsgID
|
||||
@ -161,7 +161,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
||||
}
|
||||
}
|
||||
if !isSend {
|
||||
prom_metrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
return nil, nil
|
||||
} else {
|
||||
if err = callbackBeforeSendSingleMsg(ctx, req); err != nil {
|
||||
@ -171,7 +171,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
||||
return nil, err
|
||||
}
|
||||
if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
|
||||
prom_metrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
return nil, err
|
||||
}
|
||||
err = callbackAfterSendSingleMsg(ctx, req)
|
||||
@ -183,7 +183,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
||||
ClientMsgID: req.MsgData.ClientMsgID,
|
||||
SendTime: req.MsgData.SendTime,
|
||||
}
|
||||
prom_metrics.SingleChatMsgProcessSuccessCounter.Inc()
|
||||
prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,12 +16,14 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
//func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
@ -54,7 +56,8 @@ import (
|
||||
// continue
|
||||
// }
|
||||
// if len(seqs) > 0 {
|
||||
// if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": now}); err != nil {
|
||||
// if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": now}); err
|
||||
// != nil {
|
||||
// log.ZError(ctx, "updateUsersConversationFiled failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
|
||||
// continue
|
||||
// }
|
||||
@ -104,7 +107,8 @@ func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
}
|
||||
temp := make([]*relation.ConversationModel, 0, len(conversations))
|
||||
for i, conversation := range conversations {
|
||||
if conversation.IsMsgDestruct && conversation.MsgDestructTime != 0 && (time.Now().Unix() > (conversation.MsgDestructTime+conversation.LatestMsgDestructTime.Unix()+8*60*60)) || conversation.LatestMsgDestructTime.IsZero() {
|
||||
if conversation.IsMsgDestruct && conversation.MsgDestructTime != 0 && (time.Now().Unix() > (conversation.MsgDestructTime+conversation.LatestMsgDestructTime.Unix()+8*60*60)) ||
|
||||
conversation.LatestMsgDestructTime.IsZero() {
|
||||
temp = append(temp, conversations[i])
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,13 +17,18 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
|
||||
func StartTask() error {
|
||||
@ -32,23 +37,75 @@ func StartTask() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgTool.ConvertTools()
|
||||
c := cron.New()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
msgTool.convertTools()
|
||||
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// register cron tasks
|
||||
var crontab = cron.New()
|
||||
log.ZInfo(context.Background(), "start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime)
|
||||
_, err = c.AddFunc(config.Config.ChatRecordsClearTime, msgTool.AllConversationClearMsgAndFixSeq)
|
||||
_, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
|
||||
if err != nil {
|
||||
log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime)
|
||||
_, err = c.AddFunc(config.Config.MsgDestructTime, msgTool.ConversationsDestructMsgs)
|
||||
_, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
|
||||
if err != nil {
|
||||
log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err)
|
||||
panic(err)
|
||||
}
|
||||
c.Start()
|
||||
wg.Wait()
|
||||
|
||||
// start crontab
|
||||
crontab.Start()
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
<-sigs
|
||||
|
||||
// stop crontab, Wait for the running task to exit.
|
||||
ctx := crontab.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// graceful exit
|
||||
|
||||
case <-time.After(15 * time.Second):
|
||||
// forced exit on timeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// netlock redis lock.
|
||||
func netlock(rdb redis.UniversalClient, key string, ttl time.Duration) bool {
|
||||
value := "used"
|
||||
ok, err := rdb.SetNX(context.Background(), key, value, ttl).Result() // nolint
|
||||
if err != nil {
|
||||
// when err is about redis server, return true.
|
||||
return false
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func cronWrapFunc(rdb redis.UniversalClient, key string, fn func()) func() {
|
||||
enableCronLocker := config.Config.EnableCronLocker
|
||||
return func() {
|
||||
// if don't enable cron-locker, call fn directly.
|
||||
if !enableCronLocker {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
// when acquire redis lock, call fn().
|
||||
if netlock(rdb, key, 5*time.Second) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
internal/tools/cron_task_test.go
Normal file
81
internal/tools/cron_task_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
@ -17,11 +17,15 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/discovery_register"
|
||||
"math"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"math"
|
||||
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
|
||||
"math/rand"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
@ -29,7 +33,6 @@ import (
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"math/rand"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
@ -73,7 +76,7 @@ func InitMsgTool() (*MsgTool, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
discov, err := discovery_register.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
discov, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
|
||||
/*
|
||||
discov, err := zookeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema,
|
||||
zookeeper.WithFreq(time.Hour), zookeeper.WithRoundRobin(), zookeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username,
|
||||
|
||||
@ -22,7 +22,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
)
|
||||
|
||||
func (c *MsgTool) ConvertTools() {
|
||||
func (c *MsgTool) convertTools() {
|
||||
ctx := mcontext.NewCtx("convert")
|
||||
conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
|
||||
if err != nil {
|
||||
|
||||
@ -18,40 +18,83 @@ import (
|
||||
sdkws "github.com/OpenIMSDK/protocol/sdkws"
|
||||
)
|
||||
|
||||
// SendMsg defines the structure for sending messages with various metadata.
|
||||
type SendMsg struct {
|
||||
SendID string `json:"sendID" binding:"required"`
|
||||
GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"`
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
SenderFaceURL string `json:"senderFaceURL"`
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"`
|
||||
ContentType int32 `json:"contentType" binding:"required"`
|
||||
SessionType int32 `json:"sessionType" binding:"required"`
|
||||
IsOnlineOnly bool `json:"isOnlineOnly"`
|
||||
NotOfflinePush bool `json:"notOfflinePush"`
|
||||
SendTime int64 `json:"sendTime"`
|
||||
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
|
||||
// SendID uniquely identifies the sender.
|
||||
SendID string `json:"sendID" binding:"required"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// SenderNickname is the nickname of the sender.
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
|
||||
// SenderFaceURL is the URL to the sender's avatar.
|
||||
SenderFaceURL string `json:"senderFaceURL"`
|
||||
|
||||
// SenderPlatformID is an integer identifier for the sender's platform.
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
|
||||
// Content is the actual content of the message, required and excluded from Swagger documentation.
|
||||
Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"`
|
||||
|
||||
// ContentType is an integer that represents the type of the content.
|
||||
ContentType int32 `json:"contentType" binding:"required"`
|
||||
|
||||
// SessionType is an integer that represents the type of session for the message.
|
||||
SessionType int32 `json:"sessionType" binding:"required"`
|
||||
|
||||
// IsOnlineOnly specifies if the message is only sent when the receiver is online.
|
||||
IsOnlineOnly bool `json:"isOnlineOnly"`
|
||||
|
||||
// NotOfflinePush specifies if the message should not trigger offline push notifications.
|
||||
NotOfflinePush bool `json:"notOfflinePush"`
|
||||
|
||||
// SendTime is a timestamp indicating when the message was sent.
|
||||
SendTime int64 `json:"sendTime"`
|
||||
|
||||
// OfflinePushInfo contains information for offline push notifications.
|
||||
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
|
||||
}
|
||||
|
||||
// SendMsgReq extends SendMsg with the requirement of RecvID when SessionType indicates a one-on-one or notification chat.
|
||||
type SendMsgReq struct {
|
||||
// RecvID uniquely identifies the receiver and is required for one-on-one or notification chat types.
|
||||
RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"`
|
||||
SendMsg
|
||||
}
|
||||
|
||||
// BatchSendMsgReq defines the structure for sending a message to multiple recipients.
|
||||
type BatchSendMsgReq struct {
|
||||
SendMsg
|
||||
IsSendAll bool `json:"isSendAll"`
|
||||
RecvIDs []string `json:"recvIDs" binding:"required"`
|
||||
|
||||
// IsSendAll indicates whether the message should be sent to all users.
|
||||
IsSendAll bool `json:"isSendAll"`
|
||||
|
||||
// RecvIDs is a slice of receiver identifiers to whom the message will be sent, required field.
|
||||
RecvIDs []string `json:"recvIDs" binding:"required"`
|
||||
}
|
||||
|
||||
// BatchSendMsgResp contains the results of a batch message send operation.
|
||||
type BatchSendMsgResp struct {
|
||||
Results []*SingleReturnResult `json:"results"`
|
||||
FailedIDs []string `json:"failedUserIDs"`
|
||||
// Results is a slice of SingleReturnResult, representing the outcome of each message sent.
|
||||
Results []*SingleReturnResult `json:"results"`
|
||||
|
||||
// FailedIDs is a slice of user IDs for whom the message send failed.
|
||||
FailedIDs []string `json:"failedUserIDs"`
|
||||
}
|
||||
|
||||
// SingleReturnResult encapsulates the result of a single message send attempt.
|
||||
type SingleReturnResult struct {
|
||||
// ServerMsgID is the message identifier on the server-side.
|
||||
ServerMsgID string `json:"serverMsgID"`
|
||||
|
||||
// ClientMsgID is the message identifier on the client-side.
|
||||
ClientMsgID string `json:"clientMsgID"`
|
||||
SendTime int64 `json:"sendTime"`
|
||||
RecvID string `json:"recvID"`
|
||||
|
||||
// SendTime is the timestamp of when the message was sent.
|
||||
SendTime int64 `json:"sendTime"`
|
||||
|
||||
// RecvID uniquely identifies the receiver of the message.
|
||||
RecvID string `json:"recvID"`
|
||||
}
|
||||
|
||||
51
pkg/common/cmd/msg_gateway_test.go
Normal file
51
pkg/common/cmd/msg_gateway_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
// MockRootCmd is a mock type for the RootCmd type
|
||||
type MockRootCmd struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockRootCmd) Execute() error {
|
||||
args := m.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestMsgGatewayCmd_GetPortFromConfig(t *testing.T) {
|
||||
msgGatewayCmd := &MsgGatewayCmd{RootCmd: &RootCmd{}}
|
||||
tests := []struct {
|
||||
portType string
|
||||
want int
|
||||
}{
|
||||
{constant.FlagWsPort, 8080}, // Replace 8080 with the expected port from the config
|
||||
{constant.FlagPort, 8081}, // Replace 8081 with the expected port from the config
|
||||
{"invalid", 0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.portType, func(t *testing.T) {
|
||||
got := msgGatewayCmd.GetPortFromConfig(tt.portType)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,7 @@ import (
|
||||
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
_ "go.uber.org/automaxprocs"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
|
||||
@ -78,17 +78,20 @@ type configStruct struct {
|
||||
} `yaml:"mongo"`
|
||||
|
||||
Redis struct {
|
||||
ClusterMode bool `yaml:"clusterMode"`
|
||||
Address []string `yaml:"address"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ClusterMode bool `yaml:"clusterMode"`
|
||||
Address []string `yaml:"address"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
EnablePipeline bool `yaml:"enablePipeline"`
|
||||
} `yaml:"redis"`
|
||||
|
||||
Kafka struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Addr []string `yaml:"addr"`
|
||||
TLS *struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ProducerAck string `yaml:"producerAck"`
|
||||
CompressType string `yaml:"compressType"`
|
||||
Addr []string `yaml:"addr"`
|
||||
TLS *struct {
|
||||
CACrt string `yaml:"caCrt"`
|
||||
ClientCrt string `yaml:"clientCrt"`
|
||||
ClientKey string `yaml:"clientKey"`
|
||||
@ -229,6 +232,7 @@ type configStruct struct {
|
||||
ChatRecordsClearTime string `yaml:"chatRecordsClearTime"`
|
||||
MsgDestructTime string `yaml:"msgDestructTime"`
|
||||
Secret string `yaml:"secret"`
|
||||
EnableCronLocker bool `yaml:"enableCronLocker"`
|
||||
TokenPolicy struct {
|
||||
Expire int64 `yaml:"expire"`
|
||||
} `yaml:"tokenPolicy"`
|
||||
|
||||
@ -49,7 +49,7 @@ func GetDefaultConfigPath() string {
|
||||
func GetProjectRoot() string {
|
||||
b, _ := filepath.Abs(os.Args[0])
|
||||
|
||||
return filepath.Join(filepath.Dir(b), "../../..")
|
||||
return filepath.Join(filepath.Dir(b), "../../../../..")
|
||||
}
|
||||
|
||||
func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options {
|
||||
@ -67,6 +67,7 @@ func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options {
|
||||
opts = msgprocessor.WithOptions(opts, msgprocessor.WithHistory(true), msgprocessor.WithPersistent())
|
||||
}
|
||||
opts = msgprocessor.WithOptions(opts, msgprocessor.WithSendMsg(cfg.IsSendMsg))
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
@ -89,6 +90,7 @@ func initConfig(config interface{}, configName, configFolderPath string) error {
|
||||
return fmt.Errorf("unmarshal yaml error: %w", err)
|
||||
}
|
||||
fmt.Println("use config", configFolderPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
117
pkg/common/config/parse_test.go
Normal file
117
pkg/common/config/parse_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
// 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.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
)
|
||||
|
||||
func TestGetDefaultConfigPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetDefaultConfigPath(); got != tt.want {
|
||||
t.Errorf("GetDefaultConfigPath() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProjectRoot(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetProjectRoot(); got != tt.want {
|
||||
t.Errorf("GetProjectRoot() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOptionsByNotification(t *testing.T) {
|
||||
type args struct {
|
||||
cfg NotificationConf
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want msgprocessor.Options
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetOptionsByNotification(tt.args.cfg); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetOptionsByNotification() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_initConfig(t *testing.T) {
|
||||
type args struct {
|
||||
config interface{}
|
||||
configName string
|
||||
configFolderPath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := initConfig(tt.args.config, tt.args.configName, tt.args.configFolderPath); (err != nil) != tt.wantErr {
|
||||
t.Errorf("initConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitConfig(t *testing.T) {
|
||||
type args struct {
|
||||
configFolderPath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := InitConfig(tt.args.configFolderPath); (err != nil) != tt.wantErr {
|
||||
t.Errorf("InitConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
10
pkg/common/db/cache/init_redis.go
vendored
10
pkg/common/db/cache/init_redis.go
vendored
@ -28,12 +28,21 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
var (
|
||||
// singleton pattern.
|
||||
redisClient redis.UniversalClient
|
||||
)
|
||||
|
||||
const (
|
||||
maxRetry = 10 // number of retries
|
||||
)
|
||||
|
||||
// NewRedis Initialize redis connection.
|
||||
func NewRedis() (redis.UniversalClient, error) {
|
||||
if redisClient != nil {
|
||||
return redisClient, nil
|
||||
}
|
||||
|
||||
if len(config.Config.Redis.Address) == 0 {
|
||||
return nil, errors.New("redis address is empty")
|
||||
}
|
||||
@ -66,5 +75,6 @@ func NewRedis() (redis.UniversalClient, error) {
|
||||
return nil, fmt.Errorf("redis ping %w", err)
|
||||
}
|
||||
|
||||
redisClient = rdb
|
||||
return rdb, err
|
||||
}
|
||||
|
||||
3
pkg/common/db/cache/meta_cache.go
vendored
3
pkg/common/db/cache/meta_cache.go
vendored
@ -18,9 +18,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/OpenIMSDK/tools/mw/specialerror"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mw/specialerror"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
|
||||
183
pkg/common/db/cache/msg.go
vendored
183
pkg/common/db/cache/msg.go
vendored
@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
|
||||
|
||||
@ -62,6 +63,8 @@ const (
|
||||
uidPidToken = "UID_PID_TOKEN_STATUS:"
|
||||
)
|
||||
|
||||
var concurrentLimit = 3
|
||||
|
||||
type SeqCache interface {
|
||||
SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error
|
||||
GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
|
||||
@ -345,85 +348,165 @@ func (c *msgCache) allMessageCacheKey(conversationID string) string {
|
||||
}
|
||||
|
||||
func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
if config.Config.Redis.EnablePipeline {
|
||||
return c.PipeGetMessagesBySeq(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
return c.ParallelGetMessagesBySeq(ctx, conversationID, seqs)
|
||||
}
|
||||
|
||||
func (c *msgCache) PipeGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
|
||||
results := []*redis.StringCmd{}
|
||||
for _, seq := range seqs {
|
||||
res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
|
||||
results = append(results, pipe.Get(ctx, c.getMessageCacheKey(conversationID, seq)))
|
||||
}
|
||||
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil && err != redis.Nil {
|
||||
return seqMsgs, failedSeqs, errs.Wrap(err, "pipe.get")
|
||||
}
|
||||
|
||||
for idx, res := range results {
|
||||
seq := seqs[idx]
|
||||
if res.Err() != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq, "err", res.Err())
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
msg := sdkws.MsgData{}
|
||||
if err = msgprocessor.String2Pb(res, &msg); err != nil {
|
||||
if err = msgprocessor.String2Pb(res.Val(), &msg); err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Status == constant.MsgDeleted {
|
||||
failedSeqs = append(failedSeqs, seq)
|
||||
continue
|
||||
}
|
||||
|
||||
seqMsgs = append(seqMsgs, &msg)
|
||||
}
|
||||
|
||||
return
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//for _, v := range seqs {
|
||||
// // MESSAGE_CACHE:169.254.225.224_reliability1653387820_0_1
|
||||
// key := c.getMessageCacheKey(conversationID, v)
|
||||
// if err := pipe.Get(ctx, key).Err(); err != nil && err != redis.Nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
//}
|
||||
//result, err := pipe.Exec(ctx)
|
||||
//for i, v := range result {
|
||||
// cmd := v.(*redis.StringCmd)
|
||||
// if cmd.Err() != nil {
|
||||
// failedSeqs = append(failedSeqs, seqs[i])
|
||||
// } else {
|
||||
// msg := sdkws.MsgData{}
|
||||
// err = msgprocessor.String2Pb(cmd.Val(), &msg)
|
||||
// if err == nil {
|
||||
// if msg.Status != constant.MsgDeleted {
|
||||
// seqMsgs = append(seqMsgs, &msg)
|
||||
// continue
|
||||
// }
|
||||
// } else {
|
||||
// log.ZWarn(ctx, "UnmarshalString failed", err, "conversationID", conversationID, "seq", seqs[i], "msg", cmd.Val())
|
||||
// }
|
||||
// failedSeqs = append(failedSeqs, seqs[i])
|
||||
// }
|
||||
//}
|
||||
//return seqMsgs, failedSeqs, err
|
||||
}
|
||||
|
||||
func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
type entry struct {
|
||||
err error
|
||||
msg *sdkws.MsgData
|
||||
}
|
||||
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentLimit)
|
||||
|
||||
results := make([]entry, len(seqs)) // set slice len/cap to length of seqs.
|
||||
for idx, seq := range seqs {
|
||||
// closure safe var
|
||||
idx := idx
|
||||
seq := seq
|
||||
|
||||
wg.Go(func() error {
|
||||
res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := sdkws.MsgData{}
|
||||
if err = msgprocessor.String2Pb(res, &msg); err != nil {
|
||||
log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
if msg.Status == constant.MsgDeleted {
|
||||
results[idx] = entry{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
results[idx] = entry{msg: &msg}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
for idx, res := range results {
|
||||
if res.err != nil {
|
||||
failedSeqs = append(failedSeqs, seqs[idx])
|
||||
continue
|
||||
}
|
||||
|
||||
seqMsgs = append(seqMsgs, res.msg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
if config.Config.Redis.EnablePipeline {
|
||||
return c.PipeSetMessageToCache(ctx, conversationID, msgs)
|
||||
}
|
||||
return c.ParallelSetMessageToCache(ctx, conversationID, msgs)
|
||||
}
|
||||
|
||||
func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
for _, msg := range msgs {
|
||||
s, err := msgprocessor.Pb2String(msg)
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err)
|
||||
return 0, errs.Wrap(err, "pb.marshal")
|
||||
}
|
||||
|
||||
key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil {
|
||||
_ = pipe.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second)
|
||||
}
|
||||
|
||||
results, err := pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err, "pipe.set")
|
||||
}
|
||||
|
||||
for _, res := range results {
|
||||
if res.Err() != nil {
|
||||
return 0, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return len(msgs), nil
|
||||
}
|
||||
|
||||
func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
|
||||
wg := errgroup.Group{}
|
||||
wg.SetLimit(concurrentLimit)
|
||||
|
||||
for _, msg := range msgs {
|
||||
msg := msg // closure safe var
|
||||
wg.Go(func() error {
|
||||
s, err := msgprocessor.Pb2String(msg)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
err := wg.Wait()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(msgs), nil
|
||||
//pipe := c.rdb.Pipeline()
|
||||
//var failedMsgs []*sdkws.MsgData
|
||||
//for _, msg := range msgs {
|
||||
// key := c.getMessageCacheKey(conversationID, msg.Seq)
|
||||
// s, err := msgprocessor.Pb2String(msg)
|
||||
// if err != nil {
|
||||
// return 0, errs.Wrap(err)
|
||||
// }
|
||||
// err = pipe.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err()
|
||||
// if err != nil {
|
||||
// failedMsgs = append(failedMsgs, msg)
|
||||
// log.ZWarn(ctx, "set msg 2 cache failed", err, "msg", failedMsgs)
|
||||
// }
|
||||
//}
|
||||
//_, err := pipe.Exec(ctx)
|
||||
//return len(failedMsgs), err
|
||||
}
|
||||
|
||||
func (c *msgCache) getMessageDelUserListKey(conversationID string, seq int64) string {
|
||||
|
||||
251
pkg/common/db/cache/msg_test.go
vendored
Normal file
251
pkg/common/db/cache/msg_test.go
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
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])
|
||||
}
|
||||
}
|
||||
10
pkg/common/db/cache/s3.go
vendored
10
pkg/common/db/cache/s3.go
vendored
@ -2,12 +2,14 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
)
|
||||
|
||||
type ObjectCache interface {
|
||||
|
||||
@ -378,20 +378,20 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa
|
||||
}
|
||||
failedNum, err := db.cache.SetMessageToCache(ctx, conversationID, msgs)
|
||||
if err != nil {
|
||||
prom_metrics.MsgInsertRedisFailedCounter.Add(float64(failedNum))
|
||||
prommetrics.MsgInsertRedisFailedCounter.Add(float64(failedNum))
|
||||
log.ZError(ctx, "setMessageToCache error", err, "len", len(msgs), "conversationID", conversationID)
|
||||
} else {
|
||||
prom_metrics.MsgInsertRedisSuccessCounter.Inc()
|
||||
prommetrics.MsgInsertRedisSuccessCounter.Inc()
|
||||
}
|
||||
err = db.cache.SetMaxSeq(ctx, conversationID, currentMaxSeq)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "db.cache.SetMaxSeq error", err, "conversationID", conversationID)
|
||||
prom_metrics.SeqSetFailedCounter.Inc()
|
||||
prommetrics.SeqSetFailedCounter.Inc()
|
||||
}
|
||||
err2 := db.cache.SetHasReadSeqs(ctx, conversationID, userSeqMap)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "SetHasReadSeqs error", err2, "userSeqMap", userSeqMap, "conversationID", conversationID)
|
||||
prom_metrics.SeqSetFailedCounter.Inc()
|
||||
prommetrics.SeqSetFailedCounter.Inc()
|
||||
}
|
||||
return lastMaxSeq, isNew, utils.Wrap(err, "")
|
||||
}
|
||||
|
||||
@ -16,13 +16,15 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type S3Database interface {
|
||||
|
||||
@ -16,6 +16,7 @@ package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"gorm.io/gorm"
|
||||
|
||||
|
||||
@ -20,11 +20,12 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -30,6 +29,8 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
|
||||
@ -5,11 +5,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
@ -18,6 +13,13 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/minio/minio-go/v7"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
)
|
||||
|
||||
func (m *Minio) getImageThumbnailURL(ctx context.Context, name string, expire time.Duration, opt *s3.Image) (string, error) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package discovery_register
|
||||
package discoveryregister
|
||||
|
||||
import (
|
||||
"context"
|
||||
407
pkg/common/discoveryregister/discoveryregister_test.go
Normal file
407
pkg/common/discoveryregister/discoveryregister_test.go
Normal file
@ -0,0 +1,407 @@
|
||||
package discoveryregister
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestNewDiscoveryRegister(t *testing.T) {
|
||||
type args struct {
|
||||
envType string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want discoveryregistry.SvcDiscoveryRegistry
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewDiscoveryRegister(tt.args.envType)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewDiscoveryRegister() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewDiscoveryRegister() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewK8sDiscoveryRegister(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want discoveryregistry.SvcDiscoveryRegistry
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewK8sDiscoveryRegister()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewK8sDiscoveryRegister() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewK8sDiscoveryRegister() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_Register(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
serviceName string
|
||||
host string
|
||||
port int
|
||||
opts []grpc.DialOption
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if err := cli.Register(tt.args.serviceName, tt.args.host, tt.args.port, tt.args.opts...); (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.Register() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_UnRegister(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if err := cli.UnRegister(); (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.UnRegister() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_CreateRpcRootNodes(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
serviceNames []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if err := cli.CreateRpcRootNodes(tt.args.serviceNames); (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.CreateRpcRootNodes() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_RegisterConf2Registry(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
key string
|
||||
conf []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if err := cli.RegisterConf2Registry(tt.args.key, tt.args.conf); (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.RegisterConf2Registry() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_GetConfFromRegistry(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
key string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
got, err := cli.GetConfFromRegistry(tt.args.key)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.GetConfFromRegistry() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("K8sDR.GetConfFromRegistry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_GetConns(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
serviceName string
|
||||
opts []grpc.DialOption
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []*grpc.ClientConn
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
got, err := cli.GetConns(tt.args.ctx, tt.args.serviceName, tt.args.opts...)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.GetConns() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("K8sDR.GetConns() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_GetConn(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
serviceName string
|
||||
opts []grpc.DialOption
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *grpc.ClientConn
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
got, err := cli.GetConn(tt.args.ctx, tt.args.serviceName, tt.args.opts...)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("K8sDR.GetConn() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("K8sDR.GetConn() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_GetSelfConnTarget(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if got := cli.GetSelfConnTarget(); got != tt.want {
|
||||
t.Errorf("K8sDR.GetSelfConnTarget() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_AddOption(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
opts []grpc.DialOption
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
cli.AddOption(tt.args.opts...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_CloseConn(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
type args struct {
|
||||
conn *grpc.ClientConn
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
cli.CloseConn(tt.args.conn)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_GetClientLocalConns(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want map[string][]*grpc.ClientConn
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
if got := cli.GetClientLocalConns(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("K8sDR.GetClientLocalConns() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8sDR_Close(t *testing.T) {
|
||||
type fields struct {
|
||||
options []grpc.DialOption
|
||||
rpcRegisterAddr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli := &K8sDR{
|
||||
options: tt.fields.options,
|
||||
rpcRegisterAddr: tt.fields.rpcRegisterAddr,
|
||||
}
|
||||
cli.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
package ginPrometheus
|
||||
package ginprometheus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
@ -17,40 +17,42 @@ import (
|
||||
var defaultMetricPath = "/metrics"
|
||||
|
||||
// counter, counter_vec, gauge, gauge_vec,
|
||||
// histogram, histogram_vec, summary, summary_vec
|
||||
var reqCnt = &Metric{
|
||||
ID: "reqCnt",
|
||||
Name: "requests_total",
|
||||
Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||
Type: "counter_vec",
|
||||
Args: []string{"code", "method", "handler", "host", "url"}}
|
||||
// histogram, histogram_vec, summary, summary_vec.
|
||||
var (
|
||||
reqCounter = &Metric{
|
||||
ID: "reqCnt",
|
||||
Name: "requests_total",
|
||||
Description: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||
Type: "counter_vec",
|
||||
Args: []string{"code", "method", "handler", "host", "url"}}
|
||||
|
||||
var reqDur = &Metric{
|
||||
ID: "reqDur",
|
||||
Name: "request_duration_seconds",
|
||||
Description: "The HTTP request latencies in seconds.",
|
||||
Type: "histogram_vec",
|
||||
Args: []string{"code", "method", "url"},
|
||||
}
|
||||
reqDuration = &Metric{
|
||||
ID: "reqDur",
|
||||
Name: "request_duration_seconds",
|
||||
Description: "The HTTP request latencies in seconds.",
|
||||
Type: "histogram_vec",
|
||||
Args: []string{"code", "method", "url"},
|
||||
}
|
||||
|
||||
var resSz = &Metric{
|
||||
ID: "resSz",
|
||||
Name: "response_size_bytes",
|
||||
Description: "The HTTP response sizes in bytes.",
|
||||
Type: "summary"}
|
||||
resSize = &Metric{
|
||||
ID: "resSz",
|
||||
Name: "response_size_bytes",
|
||||
Description: "The HTTP response sizes in bytes.",
|
||||
Type: "summary"}
|
||||
|
||||
var reqSz = &Metric{
|
||||
ID: "reqSz",
|
||||
Name: "request_size_bytes",
|
||||
Description: "The HTTP request sizes in bytes.",
|
||||
Type: "summary"}
|
||||
reqSize = &Metric{
|
||||
ID: "reqSz",
|
||||
Name: "request_size_bytes",
|
||||
Description: "The HTTP request sizes in bytes.",
|
||||
Type: "summary"}
|
||||
|
||||
var standardMetrics = []*Metric{
|
||||
reqCnt,
|
||||
reqDur,
|
||||
resSz,
|
||||
reqSz,
|
||||
}
|
||||
standardMetrics = []*Metric{
|
||||
reqCounter,
|
||||
reqDuration,
|
||||
resSize,
|
||||
reqSize,
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
RequestCounterURLLabelMappingFn is a function which can be supplied to the middleware to control
|
||||
@ -74,7 +76,7 @@ which would map "/customer/alice" and "/customer/bob" to their template "/custom
|
||||
type RequestCounterURLLabelMappingFn func(c *gin.Context) string
|
||||
|
||||
// Metric is a definition for the name, description, type, ID, and
|
||||
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric
|
||||
// prometheus.Collector type (i.e. CounterVec, Summary, etc) of each metric.
|
||||
type Metric struct {
|
||||
MetricCollector prometheus.Collector
|
||||
ID string
|
||||
@ -84,7 +86,7 @@ type Metric struct {
|
||||
Args []string
|
||||
}
|
||||
|
||||
// Prometheus contains the metrics gathered by the instance and its path
|
||||
// Prometheus contains the metrics gathered by the instance and its path.
|
||||
type Prometheus struct {
|
||||
reqCnt *prometheus.CounterVec
|
||||
reqDur *prometheus.HistogramVec
|
||||
@ -102,7 +104,7 @@ type Prometheus struct {
|
||||
URLLabelFromContext string
|
||||
}
|
||||
|
||||
// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
|
||||
// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional).
|
||||
type PrometheusPushGateway struct {
|
||||
|
||||
// Push interval in seconds
|
||||
@ -112,7 +114,7 @@ type PrometheusPushGateway struct {
|
||||
// where JOBNAME can be any string of your choice
|
||||
PushGatewayURL string
|
||||
|
||||
// Local metrics URL where metrics are fetched from, this could be ommited in the future
|
||||
// Local metrics URL where metrics are fetched from, this could be omitted in the future
|
||||
// if implemented using prometheus common/expfmt instead
|
||||
MetricsURL string
|
||||
|
||||
@ -120,9 +122,11 @@ type PrometheusPushGateway struct {
|
||||
Job string
|
||||
}
|
||||
|
||||
// NewPrometheus generates a new set of metrics with a certain subsystem name
|
||||
// NewPrometheus generates a new set of metrics with a certain subsystem name.
|
||||
func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus {
|
||||
subsystem = "app"
|
||||
if subsystem == "" {
|
||||
subsystem = "app"
|
||||
}
|
||||
|
||||
var metricsList []*Metric
|
||||
|
||||
@ -131,16 +135,13 @@ func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus
|
||||
} else if len(customMetricsList) == 1 {
|
||||
metricsList = customMetricsList[0]
|
||||
}
|
||||
|
||||
for _, metric := range standardMetrics {
|
||||
metricsList = append(metricsList, metric)
|
||||
}
|
||||
metricsList = append(metricsList, standardMetrics...)
|
||||
|
||||
p := &Prometheus{
|
||||
MetricsList: metricsList,
|
||||
MetricsPath: defaultMetricPath,
|
||||
ReqCntURLLabelMappingFn: func(c *gin.Context) string {
|
||||
return c.Request.URL.Path
|
||||
return c.FullPath() // e.g. /user/:id , /user/:id/info
|
||||
},
|
||||
}
|
||||
|
||||
@ -150,7 +151,7 @@ func NewPrometheus(subsystem string, customMetricsList ...[]*Metric) *Prometheus
|
||||
}
|
||||
|
||||
// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
|
||||
// every pushIntervalSeconds. Metrics are fetched from metricsURL
|
||||
// every pushIntervalSeconds. Metrics are fetched from metricsURL.
|
||||
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
|
||||
p.Ppg.PushGatewayURL = pushGatewayURL
|
||||
p.Ppg.MetricsURL = metricsURL
|
||||
@ -158,13 +159,13 @@ func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushInter
|
||||
p.startPushTicker()
|
||||
}
|
||||
|
||||
// SetPushGatewayJob job name, defaults to "gin"
|
||||
// SetPushGatewayJob job name, defaults to "gin".
|
||||
func (p *Prometheus) SetPushGatewayJob(j string) {
|
||||
p.Ppg.Job = j
|
||||
}
|
||||
|
||||
// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
|
||||
// same address of the gin engine that is being used
|
||||
// same address of the gin engine that is being used.
|
||||
func (p *Prometheus) SetListenAddress(address string) {
|
||||
p.listenAddress = address
|
||||
if p.listenAddress != "" {
|
||||
@ -181,7 +182,7 @@ func (p *Prometheus) SetListenAddressWithRouter(listenAddress string, r *gin.Eng
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsPath set metrics paths
|
||||
// SetMetricsPath set metrics paths.
|
||||
func (p *Prometheus) SetMetricsPath(e *gin.Engine) {
|
||||
|
||||
if p.listenAddress != "" {
|
||||
@ -192,7 +193,7 @@ func (p *Prometheus) SetMetricsPath(e *gin.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsPathWithAuth set metrics paths with authentication
|
||||
// SetMetricsPathWithAuth set metrics paths with authentication.
|
||||
func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) {
|
||||
|
||||
if p.listenAddress != "" {
|
||||
@ -205,34 +206,43 @@ func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts
|
||||
}
|
||||
|
||||
func (p *Prometheus) runServer() {
|
||||
if p.listenAddress != "" {
|
||||
go p.router.Run(p.listenAddress)
|
||||
}
|
||||
go p.router.Run(p.listenAddress)
|
||||
}
|
||||
|
||||
func (p *Prometheus) getMetrics() []byte {
|
||||
response, _ := http.Get(p.Ppg.MetricsURL)
|
||||
response, err := http.Get(p.Ppg.MetricsURL)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return body
|
||||
}
|
||||
|
||||
var hostname, _ = os.Hostname()
|
||||
|
||||
func (p *Prometheus) getPushGatewayURL() string {
|
||||
h, _ := os.Hostname()
|
||||
if p.Ppg.Job == "" {
|
||||
p.Ppg.Job = "gin"
|
||||
}
|
||||
return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
|
||||
return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + hostname
|
||||
}
|
||||
|
||||
func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
|
||||
req, err := http.NewRequest("POST", p.getPushGatewayURL(), bytes.NewBuffer(metrics))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
if _, err = client.Do(req); err != nil {
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error sending to push gateway error:", err.Error())
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func (p *Prometheus) startPushTicker() {
|
||||
@ -244,7 +254,7 @@ func (p *Prometheus) startPushTicker() {
|
||||
}()
|
||||
}
|
||||
|
||||
// NewMetric associates prometheus.Collector based on Metric.Type
|
||||
// NewMetric associates prometheus.Collector based on Metric.Type.
|
||||
func NewMetric(m *Metric, subsystem string) prometheus.Collector {
|
||||
var metric prometheus.Collector
|
||||
switch m.Type {
|
||||
@ -321,20 +331,20 @@ func NewMetric(m *Metric, subsystem string) prometheus.Collector {
|
||||
}
|
||||
|
||||
func (p *Prometheus) registerMetrics(subsystem string) {
|
||||
|
||||
for _, metricDef := range p.MetricsList {
|
||||
metric := NewMetric(metricDef, subsystem)
|
||||
if err := prometheus.Register(metric); err != nil {
|
||||
fmt.Println("could not be registered in Prometheus,metricDef.Name:", metricDef.Name, " error:", err.Error())
|
||||
}
|
||||
|
||||
switch metricDef {
|
||||
case reqCnt:
|
||||
case reqCounter:
|
||||
p.reqCnt = metric.(*prometheus.CounterVec)
|
||||
case reqDur:
|
||||
case reqDuration:
|
||||
p.reqDur = metric.(*prometheus.HistogramVec)
|
||||
case resSz:
|
||||
case resSize:
|
||||
p.resSz = metric.(prometheus.Summary)
|
||||
case reqSz:
|
||||
case reqSize:
|
||||
p.reqSz = metric.(prometheus.Summary)
|
||||
}
|
||||
metricDef.MetricCollector = metric
|
||||
@ -353,7 +363,7 @@ func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) {
|
||||
p.SetMetricsPathWithAuth(e, accounts)
|
||||
}
|
||||
|
||||
// HandlerFunc defines handler function for middleware
|
||||
// HandlerFunc defines handler function for middleware.
|
||||
func (p *Prometheus) HandlerFunc() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.URL.Path == p.MetricsPath {
|
||||
@ -393,7 +403,7 @@ func prometheusHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
func computeApproximateRequestSize(r *http.Request) int {
|
||||
s := 0
|
||||
var s int
|
||||
if r.URL != nil {
|
||||
s = len(r.URL.Path)
|
||||
}
|
||||
154
pkg/common/http/http_client_test.go
Normal file
154
pkg/common/http/http_client_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
// 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.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
type args struct {
|
||||
url string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantResponse []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotResponse, err := Get(tt.args.url)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotResponse, tt.wantResponse) {
|
||||
t.Errorf("Get() = %v, want %v", gotResponse, tt.wantResponse)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPost(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
url string
|
||||
header map[string]string
|
||||
data interface{}
|
||||
timeout int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantContent []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotContent, err := Post(tt.args.ctx, tt.args.url, tt.args.header, tt.args.data, tt.args.timeout)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Post() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotContent, tt.wantContent) {
|
||||
t.Errorf("Post() = %v, want %v", gotContent, tt.wantContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostReturn(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
url string
|
||||
header map[string]string
|
||||
input interface{}
|
||||
output interface{}
|
||||
timeOutSecond int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := PostReturn(tt.args.ctx, tt.args.url, tt.args.header, tt.args.input, tt.args.output, tt.args.timeOutSecond); (err != nil) != tt.wantErr {
|
||||
t.Errorf("PostReturn() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_callBackPostReturn(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
url string
|
||||
command string
|
||||
input interface{}
|
||||
output callbackstruct.CallbackResp
|
||||
callbackConfig config.CallBackConfig
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := callBackPostReturn(tt.args.ctx, tt.args.url, tt.args.command, tt.args.input, tt.args.output, tt.args.callbackConfig); (err != nil) != tt.wantErr {
|
||||
t.Errorf("callBackPostReturn() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallBackPostReturn(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
url string
|
||||
req callbackstruct.CallbackReq
|
||||
resp callbackstruct.CallbackResp
|
||||
callbackConfig config.CallBackConfig
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := CallBackPostReturn(tt.args.ctx, tt.args.url, tt.args.req, tt.args.resp, tt.args.callbackConfig); (err != nil) != tt.wantErr {
|
||||
t.Errorf("CallBackPostReturn() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,10 @@
|
||||
package kafka
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
@ -49,8 +51,23 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
|
||||
p.config = sarama.NewConfig() // Instantiate a sarama Config
|
||||
p.config.Producer.Return.Successes = true // Whether to enable the successes channel to be notified after the message is sent successfully
|
||||
p.config.Producer.Return.Errors = true
|
||||
p.config.Producer.RequiredAcks = sarama.WaitForAll // Set producer Message Reply level 0 1 all
|
||||
p.config.Producer.Partitioner = sarama.NewHashPartitioner // Set the hash-key automatic hash partition. When sending a message, you must specify the key value of the message. If there is no key, the partition will be selected randomly
|
||||
|
||||
var producerAck = sarama.WaitForAll // default: WaitForAll
|
||||
switch strings.ToLower(config.Config.Kafka.ProducerAck) {
|
||||
case "no_response":
|
||||
producerAck = sarama.NoResponse
|
||||
case "wait_for_local":
|
||||
producerAck = sarama.WaitForLocal
|
||||
case "wait_for_all":
|
||||
producerAck = sarama.WaitForAll
|
||||
}
|
||||
p.config.Producer.RequiredAcks = producerAck
|
||||
|
||||
var compress = sarama.CompressionNone // default: no compress
|
||||
_ = compress.UnmarshalText(bytes.ToLower([]byte(config.Config.Kafka.CompressType)))
|
||||
p.config.Producer.Compression = compress
|
||||
|
||||
if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" {
|
||||
p.config.Net.SASL.Enable = true
|
||||
p.config.Net.SASL.User = config.Config.Kafka.Username
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user