前提:因为prisma对于增删改有事务,所以必须使用mongdb副本集群
错误描述
None of the available servers suitable for criteria Predicate. Topology: { Type: Unknown, Servers: [ { Address: localhost:27017, Type: RsGhost, Average RTT: 346.567µs, Last Update Time: DateTime(2022-02-16T20:00:59.552Z), Max Wire Version: 9, Min Wire Version: 0 }, ] }
错误描述很清楚Address: localhost:27017,没有找到合适可用的服务
.env文件
DATABASE_URL="mongodb://自己的云服务器ip地址:27018/test?authSource=admin&retryWrites=true&w=majority"
schema.prisma
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
一切都很平平无奇,下面进行排查流程,此处不做如何搭建mongodb副本集群的教程,可以百度,上面有很多教程
查看错误信息,如果是副本集群报错应该是多个数组(这里拿的是别人的错误,所以只有一个)
Topology: {
Type: Unknown,
Servers: [
{
Address: localhost:27017,
Type: RsGhost,
Average RTT: 346.567µs,
Last Update Time: DateTime(2022-02-16T20:00:59.552Z),
Max Wire Version: 9,
Min Wire Version: 0
},
]
}
可以看到address字段为localhost:port形式,那么,会不会因为prisma会向mongodb副本集群获取rs.config()然后再根据配置的host进行连接?
这里直接告诉结论:是的,prisma就是这样做的
首先查看自己的配置信息,进入mongo/mongosh,rs.config()可以发现你配置的所有副本集群信息
对host: localhost进行修改
// 进入主机节点,比如27017是主节点
mongo/mongosh localhost:27017
config = rs.config();
// 打印config信息
members: [
{
_id: 1,
host: 'localhost:27017',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long("0"),
votes: 1
},
{
_id: 2,
host: 'localhost:27018',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long("0"),
votes: 1
}
],
// 因为使用了对host都是localhost, 所以直接修改配置文件会报错:Either all host names in a replica set configuration must be localhost references, or none must be; found 1 out of 2.
// 意思就是,所有副本集的host配置要么是localhost要么都不是localhost
// 移除副本节点
rs.remove('loclhost:27018');
// 修改配置
reconfig = rs.config().members[0].host = 'ip:port'; // 要么修改成ip:端口,也可以用域名代替
rs.reconfig(reconfig);
// 查看是否修改成功
rs.config();
// 添加子节点
rs.add('ip:port'); // 或域名:端口
// 查看是否修改成功
rs.config();
// 查看状态
rs.status();
最后验证
npx prisma studio:进去后不报错就是ok
跑一个查询代码
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.$connect();
const allUsers = await prisma.user.findMany();
console.log(allUsers);
}
main()
.catch((e) => {
console.log(e);
})
.finally(async () => {
await prisma.$disconnect();
});
结果不管怎样,只要没报错就是ok的
贴上一个简单实用keyFile集群成员认证 + 客户端账号密码认证的1主2从副本集群攻略
日期:2022-07-31
docker-compose文件
version: '3.1'
services:
mongodb1:
# 默认最新
image: mongo
# 错误后自动重启
restart: on-failure
container_name: mongo1
volumes:
# 数据存放目录
- ./db/mongo1:/data/db
# keyfile:用于集群之间的认证
- /data/mongodb/mongodb.key:/data/mongodb.key
# 配置目录
- ./config/mongo1:/data/configdb
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
networks:
- mongoNet
command: mongod --replSet rs0 --keyFile /data/mongodb.key
entrypoint:
- bash
- -c
- |
chmod 400 /data/mongodb.key
chown 999:999 /data/mongodb.key
exec docker-entrypoint.sh $$@
mongodb2:
image: mongo
restart: on-failure
container_name: mongo2
volumes:
- ./db/mongo2:/data/db
- /data/mongodb/mongodb.key:/data/mongodb.key
- ./config/mongo2:/data/configdb
ports:
- 27018:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
networks:
- mongoNet
command: mongod --replSet rs0 --keyFile /data/mongodb.key
entrypoint:
- bash
- -c
- |
chmod 400 /data/mongodb.key
chown 999:999 /data/mongodb.key
exec docker-entrypoint.sh $$@
mongodb3:
image: mongo
restart: on-failure
container_name: mongo3
volumes:
- ./db/mongo3:/data/db
- /data/mongodb/mongodb.key:/data/mongodb.key
- ./config/mongo3:/data/configdb
ports:
- 27019:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
networks:
- mongoNet
command: mongod --replSet rs0 --keyFile /data/mongodb.key
entrypoint:
- bash
- -c
- |
chmod 400 /data/mongodb.key
chown 999:999 /data/mongodb.key
exec docker-entrypoint.sh $$@
networks:
mongoNet:
driver: bridge
进入节点:docker exec -it mongo1 /bin/bash
rs.initiate({
// 集群名称,一定要和配置文件保持一致
_id: "rs0",
members: [
// 一定要用公网,否则访问不到
{ _id: 0, host: "xxxxxxxx:port" },
{ _id: 1, host: "xxxxxxxx:port" },
{ _id: 2, host: "xxxxxxxx:port" }
]
});
测试,使用gui通过账号密码登陆测试
// 我用的DataGrip(all in one 工具) 你可以用navicat、stdio 3T
mongodb://admin:admin@xxx:port1,xxx:port2,xxx:port3/test?replicaSet=rs0&authSource=admin&slaveOk=true&connectTimeoutMS=5000