首页 > 其他分享 >深入理解 ZooKeeper的ACL实现

深入理解 ZooKeeper的ACL实现

时间:2022-10-19 10:03:33浏览次数:55  
标签:ZooKeeper request cnxn ACL 深入 new null todo digest

2020-02-08 补充流程图

深入理解 ZooKeeper的ACL实现_服务端

如果对您有帮助,欢迎点赞支持, 如果有不对的地方,欢迎指出批评

什么是ACL(Access Control List)

zookeeper在分布式系统中承担中间件的作用,它管理的每一个节点上可能都存储这重要的信息,因为应用可以读取到任意节点,这就可能造成安全问题,ACL的作用就是帮助zookeeper实现权限控制, 比如对节点的增删改查

​点击查上篇博客中客户端使用acl的详解​

addAuth客户端源码追踪入口

通过前几篇博客的追踪我们知道了,客户端启动三条线程,如下

  • 守护线程 sendThread 负责客户端和服务端的IO通信
  • 守护线程 EventThread 负责处理服务端和客户端有关事务的事件
  • 主线程 负责解析处理用户在控制台的输入

所以本篇博客的客户端入口选取的是客户端的主程序​​processZKCmd(MyCommandOptions co)​​, 源码如下

protected boolean processZKCmd(MyCommandOptions co) throws KeeperException, IOException, InterruptedException {
// todo 在这个方法中可以看到很多的命令行所支持的命令
Stat stat = new Stat();
// todo 获取命令行输入中 0 1 2 3 ... 位置的内容, 比如 0 位置是命令 1 2 3 位置可能就是不同的参数
String[] args = co.getArgArray();
String cmd = co.getCommand();
if (args.length < 1) {
usage();
return false;
}

if (!commandMap.containsKey(cmd)) {
usage();
return false;
}

boolean watch = args.length > 2;
String path = null;
List<ACL> acl = Ids.OPEN_ACL_UNSAFE;
LOG.debug("Processing " + cmd);

if (cmd.equals("quit")) {
System.out.println("Quitting...");
zk.close();
System.exit(0);
}
.
.
.
.
} else if (cmd.equals("addauth") && args.length >= 2) {
byte[] b = null;
if (args.length >= 3)
b = args[2].getBytes();

zk.addAuthInfo(args[1], b);
} else if (!commandMap.containsKey(cmd)) {
usage();
}
return watch;
}

假如说我们是想在服务端的上下文中添加一个授权的信息, 假设我们这样写addauth digest lisi:123123​​,这条命令经过主线程处理之后就来到上述源码的​​else if (cmd.equals("addauth") && args.length >= 2)​​​部分, 然后调用了ZooKeeper.java的​​ zk.addAuthInfo(args[1], b);​​ 源码如下:

public void addAuthInfo(String scheme, byte auth[]) {
cnxn.addAuthInfo(scheme, auth);
}

继续跟进​​ClientCnxn​​​的​​addAuthInfo()​​方法,源码如下 它主要做了两件事:

  • 将sheme + auth 进行了封装
  • 然后将seheme + auth 封装进了封装进Request,在经过​​queuePacket()​​方法封装进packet,添加到outgoingQueue中等待sendThread将其消费发送服务端
public void addAuthInfo(String scheme, byte auth[]) {
if (!state.isAlive()) {
return;
}
// todo 将用户输入的权限封装进 AuthData
// todo 这也是ClientCnxn的内部类
authInfo.add(new AuthData(scheme, auth));

// todo 封装进一个request中
queuePacket(new RequestHeader(-4, OpCode.auth), null,
new AuthPacket(0, scheme, auth), null, null, null, null,
null, null);
}

addAuth服务端的入口

在服务端去处理客户端请求的是三个​​Processor​​ 分别是:

  • PrepRequestProcessor 负责更新状态
  • SyncRequestProcessor 同步处理器,主要负责将事务持久化
  • FinalRequestProcessor 主要负责响应客户端

服务端选取的入口是 ​​NIOServerCnxn.java​​​的​​readRequest()​​, 源码如下:

// todo 解析客户端传递过来的packet
private void readRequest() throws IOException {
// todo ,跟进去看zkserver 如何处理packet
zkServer.processPacket(this, incomingBuffer);
}

继续跟进​​processPacket()​​,源码如下:

虽然这段代码也挺长的,但是它的逻辑很清楚,

  • 将客户端发送过来的数据反序列化进new出来的RequestHeader
  • 跟进RequestHeader判断是否需要auth鉴定
  • 需要:
  • 创建AuthPacket对象,将数据反序列化进它里面
  • 使用​​AuthenticationProvider​​进行权限验证
  • 如果成功了返回​​KeeperException.Code.OK​​其他的状态是抛出异常中断操作
  • 不需要
  • 将客户端端发送过来的数据封装进Request
  • 将Request扔向请求处理链进一步处理

深入理解 ZooKeeper的ACL实现_ide_02

其中​​AuthenticationProvider​​在这里设计的很好,他是个接口,针对不同的schme它有不同的实现子类,这样当前的​​ap.handleAuthentication(cnxn, authPacket.getAuth());​​ 一种写法,就可以实现多种不同的动作

// todo  在ZKserver中解析客户端发送过来的request
public void processPacket(ServerCnxn cnxn, ByteBuffer incomingBuffer) throws IOException {
// We have the request, now process and setup for next
// todo 从bytebuffer中读取数据, 解析封装成 RequestHeader
InputStream bais = new ByteBufferInputStream(incomingBuffer);
BinaryInputArchive bia = BinaryInputArchive.getArchive(bais);
RequestHeader h = new RequestHeader();
// todo 对RequestHeader 进行反序列化
h.deserialize(bia, "header");

// Through the magic of byte buffers, txn will not be pointing to the start of the txn
// todo
incomingBuffer = incomingBuffer.slice();
// todo 对应用户在命令行敲的 addauth命令
// todo 这次专程为了 探究auth而来
if (h.getType() == OpCode.auth) {
LOG.info("got auth packet " + cnxn.getRemoteSocketAddress());
// todo 创建AuthPacket,将客户端发送过来的数据反序列化进 authPacket对象中
/** 下面的authPacket的属性
* private int type;
* private String scheme;
* private byte[] auth;
*/
AuthPacket authPacket = new AuthPacket();
ByteBufferInputStream.byteBuffer2Record(incomingBuffer, authPacket);


String scheme = authPacket.getScheme();
AuthenticationProvider ap = ProviderRegistry.getProvider(scheme);

Code authReturn = KeeperException.Code.AUTHFAILED;
if(ap != null) {
try {
// todo 来到这里进一步处理, 跟进去
// todo AuthenticationProvider 有很多三个实现实现类, 分别处理不同的 Auth , 我们直接跟进去digest类中
authReturn = ap.handleAuthentication(cnxn, authPacket.getAuth());
} catch(RuntimeException e) {
LOG.warn("Caught runtime exception from AuthenticationProvider: " + scheme + " due to " + e);
authReturn = KeeperException.Code.AUTHFAILED;
}
}
if (authReturn!= KeeperException.Code.OK) {
if (ap == null) {
LOG.warn("No authentication provider for scheme: "
+ scheme + " has "
+ ProviderRegistry.listProviders());
} else {
LOG.warn("Authentication failed for scheme: " + scheme);
}
// send a response...
ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
KeeperException.Code.AUTHFAILED.intValue());
cnxn.sendResponse(rh, null, null);
// ... and close connection
cnxn.sendBuffer(ServerCnxnFactory.closeConn);
cnxn.disableRecv();
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Authentication succeeded for scheme: "
+ scheme);
}
LOG.info("auth success " + cnxn.getRemoteSocketAddress());
ReplyHeader rh = new ReplyHeader(h.getXid(), 0,
KeeperException.Code.OK.intValue());
cnxn.sendResponse(rh, null, null);
}
return;
} else {
if (h.getType() == OpCode.sasl) {
Record rsp = processSasl(incomingBuffer,cnxn);
ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.OK.intValue());
cnxn.sendResponse(rh,rsp, "response"); // not sure about 3rd arg..what is it?
return;
}
else {
// todo 将上面的信息包装成 request
Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(), h.getType(), incomingBuffer, cnxn.getAuthInfo());
si.setOwner(ServerCnxn.me);
// todo 提交request, 其实就是提交给服务端的 process处理器进行处理
submitRequest(si);
}
}
cnxn.incrOutstandingRequests(h);
}

因为我们的重点是查看ACL的实现机制,所以继续跟进 ​​ap.handleAuthentication(cnxn, authPacket.getAuth());​​(选择DigestAuthenticationProvier的实现) 源码如下:

这个方法算是核心方法, 主要了做了如下几件事

  • 我们选择的是Digest模式,针对用户的输入​​lisi:123123​​ 这部分信息生成数字签名
  • 如果这个用户是超级用户的话,在ServerCnxn维护的authInfo中添加​​super : ''​​ 比较是超级管理员
  • 将当前的信息封装进Id对象,添加到 authInfo
  • 认证成功?
  • 返回​​ KeeperException.Code.OK;​
  • 认证失败
  • 返回​​KeeperException.Code.AUTHFAILED;​
public KeeperException.Code
handleAuthentication(ServerCnxn cnxn, byte[] authData) {
String id = new String(authData);
try {
// todo 生成一个签名, 跟进去看看下 签名的处理步骤, 就在上面
String digest = generateDigest(id);
if (digest.equals(superDigest)) { // todo 从这个可以看出, zookeeper是存在超级管理员用户的, 跟进去看看 superDigest 其实就是读取配置文件得来的
//todo 满足这个条件就会在这个list中多存一个权限
cnxn.addAuthInfo(new Id("super", ""));
}
// todo 将scheme + digest 添加到cnxn的AuthInfo中 ,
cnxn.addAuthInfo(new Id(getScheme(), digest));
// todo 返回认证成功的标识
return KeeperException.Code.OK;
} catch (NoSuchAlgorithmException e) {
LOG.error("Missing algorithm", e);
}
return KeeperException.Code.AUTHFAILED;
}

authInfo有啥用?

它其实是一个List数组,存在于内存中,一旦客户端关闭了这个数组中存放的内容就全部丢失了

一般我们是这么玩的,比如,我们创建了一个node,但是不想让任何一个人都能访问他里面的数据,于是我们就他给添加一组ACL权限, 就像下面这样

# 创建节点
[zk: localhost:2181(CONNECTED) 0] create /node2 2
Created /node2

# 添加一个用户
[zk: localhost:2181(CONNECTED) 1] addauth digest lisi:123123
# 给这个node2节点设置一个;lisi的用户,只有这个lisi才拥有node的全部权限
[zk: localhost:2181(CONNECTED) 2] setAcl /node2 auth:lisi:cdrwa
cZxid = 0x2d7
ctime = Fri Sep 27 08:19:58 CST 2019
mZxid = 0x2d7
mtime = Fri Sep 27 08:19:58 CST 2019
pZxid = 0x2d7
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0

[zk: localhost:2181(CONNECTED) 3] getAcl /node2
'digest,'lisi:dcaK2UREXUmcqg6z9noXkh1bFaM=
: cdrwa

这时候断开客户端的连接, 打开一个新的连接,重试get

# 会发现已经没有权限了
[zk: localhost:2181(CONNECTED) 1] getAcl /node2
Authentication is not valid : /node2

# 重新添加auth
[zk: localhost:2181(CONNECTED) 2] addauth digest lisi:123123
[zk: localhost:2181(CONNECTED) 3] getAcl /node2
'digest,'lisi:dcaK2UREXUmcqg6z9noXkh1bFaM=
: cdrwa

可以看到,经过本轮操作后,node2节点有了已经被持久化的特征,lisi才能对他有全部权限,这么看addauth digest lisi:123123就有点添加了一个用户的概念,只不过这个信息最终会存放在上面提到的authInfo中, 这也是为啥一旦重启了,想要访问得重新添加权限的原因

言归正传,接着看上面的函数,我们看它是如何进行签名的, 拿lisi:123123举例子

  • 使用:分隔
  • 将后半部分的123123经过SHA1加密
  • 再进行BASE64加密
  • 最后拼接 lisi:sugsduyfgyuadgfuyadadfgba...
// todo 签名的处理步骤
static public String generateDigest(String idPassword)
throws NoSuchAlgorithmException {
//todo 根据: 分隔
String parts[] = idPassword.split(":", 2);
//todo 先用SHA1进行加密
byte digest[] = MessageDigest.getInstance("SHA1").digest(
idPassword.getBytes());
//todo 再用BASE64进行加密
// todo username:签名
return parts[0] + ":" + base64Encode(digest);
}

加密完成后有样的判断,证明zookeeper中是有超级管理员角色存在的

if (digest.equals(superDigest)) { // todo 从这个可以看出, zookeeper是存在超级管理员用户的, 跟进去看看 superDigest 其实就是读取配置文件得来的
//todo 满足这个条件就会在这个list中多存一个权限
cnxn.addAuthInfo(new Id("super", ""));
}

点击superDisgest,他是这样介绍的

/** specify a command line property with key of 
* "zookeeper.DigestAuthenticationProvider.superDigest"
* and value of "super:<base64encoded(SHA1(password))>" to enable
* super user access (i.e. acls disabled)
*/
// todo 在命令行中指定 key = zookeeper.DigestAuthenticationProvider.superDigest
// todo 指定value = super:<base64encoded(SHA1(password))>
// todo 就可以开启超级管理员用户
private final static String superDigest = System.getProperty(
"zookeeper.DigestAuthenticationProvider.superDigest");

小结:

到目前为止,我们就知道了addauth在底层源码做出了哪些动作,以及服务端将我们手动添加进来的权限信息都放在内存中


setACL源码追踪入口

同样会和addAuth操作一样,主线程从控制台解析出用户的请求封装进request然后封装进pakcet发送给服务端

setACL服务端的处理逻辑

请求来到服务端,在遇到第一次checkAcl之间,请求会顺利的来到第一个处理器​​PrepRequestProcessor​​, 所以我们的入口点就是这里

protected void pRequest(Request request) throws RequestProcessorException {
// LOG.info("Prep>>> cxid = " + request.cxid + " type = " +
// request.type + " id = 0x" + Long.toHexString(request.sessionId));
request.hdr = null;
request.txn = null;
// todo 下面的不同类型的信息, 对应这不同的处理器方式
try {
switch (request.type) {
case OpCode.create:
// todo 创建每条记录对应的bean , 现在还是空的, 在面的pRequest2Txn 完成赋值
CreateRequest createRequest = new CreateRequest();
// todo 跟进这个方法, 再从这个方法出来,往下运行,可以看到调用了下一个处理器
pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest, true);
break;
case OpCode.delete:
DeleteRequest deleteRequest = new DeleteRequest();
pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest, true);
break;
case OpCode.setData:
SetDataRequest setDataRequest = new SetDataRequest();
pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest, true);
break;
case OpCode.setACL:
// todo 客户端发送的setAcl命令, 会流经这个选项
SetACLRequest setAclRequest = new SetACLRequest();
/** SetACLRequest的属性
* private String path;
* private java.util.List<org.apache.zookeeper.data.ACL> acl;
* private int version;
*/
// todo 继续跟进去
pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest, true);
break;
case OpCode.check:

用户在控制台输入类似 ​​setAcl /node4 digest:zhangsan:jA/7JI9gsuLp0ZQn5J5dcnDQkHA=​​​ 请求将被解析运行到上面的​​ case OpCode.setACL:​它new了一个空的对象SetACLRequest,这个对象一会在pRequest2Txn()函数中进行初始化

继续跟进​​pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest, true);​​ 源码如下: 它的解析我写在这段代码的下面

protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize)
throws KeeperException, IOException, RequestProcessorException
{
// todo 使用request的相关属性,创建出 事务Header
request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid,
Time.currentWallTime(), type);

switch (type) {
case OpCode.create:
// todo 校验session的情况
zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
CreateRequest createRequest = (CreateRequest)record;
.
.
.
case OpCode.setACL:
// todo 检查session的合法性
zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
// todo record; 上一步中new 出来的SetACLRequest空对象,
// todo 这样设计的好处就是, 可以进行横向的扩展, 让当前这个方法 PRequest2Tm()中可以被Record的不同实现类复用
SetACLRequest setAclRequest = (SetACLRequest)record;
// todo 将结果反序列化进 setAclRequest
if(deserialize)
ByteBufferInputStream.byteBuffer2Record(request.request, setAclRequest);

// todo 获取path 并校验
path = setAclRequest.getPath();
validatePath(path, request.sessionId);

// todo 去除重复的acl
listACL = removeDuplicates(setAclRequest.getAcl());
if (!fixupACL(request.authInfo, listACL)) {
// todo request.authInfo的默认值就是本地ip, 如果没有这个值的话,在server本地,client都连接不上
throw new KeeperException.InvalidACLException(path);
}
//todo 获取当前节点的record
nodeRecord = getRecordForPath(path);
// todo 共用的checkACL 方法
// todo 在setAcl时,使用checkACL进行权限的验证
// todo nodeRecord.acl 当前节点的acl
// todo 跟进这个方法
checkACL(zks, nodeRecord.acl, ZooDefs.Perms.ADMIN,
request.authInfo);
version = setAclRequest.getVersion();
currentVersion = nodeRecord.stat.getAversion();
if (version != -1 && version != currentVersion) {
throw new KeeperException.BadVersionException(path);
}
version = currentVersion + 1;
request.txn = new SetACLTxn(path, listACL, version);
nodeRecord = nodeRecord.duplicate(request.hdr.getZxid());
nodeRecord.stat.setAversion(version);
addChangeRecord(nodeRecord);
break;
// todo createSession/////////////////////////////////////////////////////////////////
case OpCode.createSession:
.
.
.
  • 先说一下有个亮点, 就是这个函数中倒数第二个参数位置写着需要的参数是record类型的,但是实际上我们传递进来的类型是​​SetACLRequest​​​上面的这个空对象​​SetACLRequest​​这样的设计使得的扩展性变得超级强

这是record的类图

深入理解 ZooKeeper的ACL实现_客户端_03

言归正传,来到这个函数算是进入了第二个高潮, 他主要做了这几件事

  • 检查session是否合法
  • 将数据反序列化进​​SetACLRequest​
  • 校验path是否合法
  • 去除重复的acl
  • CheckAcl鉴权

我们重点看最后两个地方

去除重复的acl

​fixupACL(request.authInfo, listACL)​

这个函数很有趣,举个例子,通过控制台,我们连接上一个服务端,然后通过如下命令往服务端的authInfo集合中添加三条数据

addauth digest lisi1:1 
addauth digest lisi2:2
addauth digest lisi3:3

然后给lisi授予针对node1的权限

setAcl /node auth:lisi1:123123:adr

再次查看,会发现lisi2 lisi3同样有了对node1的权限

CheckAcl鉴权

​checkACL(zks, nodeRecord.acl, ZooDefs.Perms.ADMIN,request.authInfo);​​ 源码如下:

这个函数的主要逻辑就是,从头到尾的执行,只要满足了合法的权限就退出,否则运行到最后都没有合法的权限,就抛出没有授权的异常从而中断请求,如果正常返回了,说明权限经过了验证,既然经过了验证request就可以继续在process链上运行,进一步进行处理

static void checkACL(ZooKeeperServer zks, List<ACL> acl, int perm,
List<Id> ids) throws KeeperException.NoAuthException {
// todo 这是个写在配置文件中的 配置属性 zookeeper.skipACL , 可以关闭acl验证
if (skipACL) {
return;
}
// todo 当前的节点没有任何验证的规则的话,直接通过
if (acl == null || acl.size() == 0) {
return;
}
// todo 如果ids中存放着spuer 超级用户,也直接通过
for (Id authId : ids) {
if (authId.getScheme().equals("super")) {
return;
}
}
// todo 循环当前节点上存在的acl点
for (ACL a : acl) {
Id id = a.getId();
// todo 使用& 位运算 , 去ZooDefs类看看位移的情况
// todo 如果设置的权限为 a.getPerms() =dra = d+r+a = 8+1+16 = 25
// todo perm = 16
/**
* 进行&操作
* 25 & 16
* 11001
* 10000
* 结果
* 10000
* 结果不是0 ,进入if { }
*/
if ((a.getPerms() & perm) != 0) {
if (id.getScheme().equals("world")
&& id.getId().equals("anyone")) {
return;
}

AuthenticationProvider ap = ProviderRegistry.getProvider(id.getScheme());

if (ap != null) {
for (Id authId : ids) {
if (authId.getScheme().equals(id.getScheme())
&& ap.matches(authId.getId(), id.getId())) {
return;
}
}
}
}
}
//todo 到最后也没返回回去, 就抛出异常
throw new KeeperException.NoAuthException();
}

几个重要的参数

  • acl
  • 当前node已经存在的 需要的权限信息​​scheme:id;​
  • perm
  • 当前用户的操作需要的权限
  • ids
  • 我们在上面通过addauth添加进authInfo列表中的信息
  • skip跳过权限验证
static boolean skipACL;
static {
skipACL = System.getProperty("zookeeper.skipACL", "no").equals("yes");
if (skipACL) {
LOG.info("zookeeper.skipACL==\"yes\", ACL checks will be skipped");
}
}

这里面在验证权限时存在位运算,prem在ZooDFS.java中维护

// todo 位移的操作
@InterfaceAudience.Public
public interface Perms {
// 左移
int READ = 1 << 0; //1 2的0次方

int WRITE = 1 << 1; //2 2的1次方

int CREATE = 1 << 2; // 4

int DELETE = 1 << 3; // 8

int ADMIN = 1 << 4; // 16

int ALL = READ | WRITE | CREATE | DELETE | ADMIN; //31

/**
* 00001
* 00010
* 00100
* 01000
* 10000
*
* 结果11111 = 31
*
*/

}

总结:

通过跟踪上面的源码,我们知道了zookeeper的权限acl是如何实现的,以及客户端和服务端之间是如何相互配合的

  • 客户端同样是经过主线程跟进不同的命令类型,将请求打包packet发送到服务端
  • 服务端将addauth添加认证信息保存在内存中
  • node会被持久化,因为它需要的认证同样被持久化
  • 在进行处理request之前,会进行checkAcl的操作,它是在第一个处理器中完成的,只有经过权限认证,request才能继续在processor链中往下传递

如果对您有帮助,欢迎点赞支持, 如果有不对的地方,欢迎指出批评



标签:ZooKeeper,request,cnxn,ACL,深入,new,null,todo,digest
From: https://blog.51cto.com/u_15311508/5768803

相关文章

  • 深入理解 ZooKeeper客户端与服务端的watcher回调
    2020-02-08补充本篇博文所描述的watcher回调的流程图watcher存在的必要性举个特容易懂的例子:假如我的项目是基于dubbo+zookeeper搭建的分布式项目,我有三个功能相同的服......
  • 数据库oracle
    14控制用户访问知识点:1)如何创建用户、修改密码;2)如何创建角色,把不同的权限分配给角色、用户;3)如何进行授权和回收权限(grant、revoke);14.1创建用户DBA使用createuse......
  • 跨平台 动态化 陈航 03-深入理解跨平台方案的历史发展逻辑
    本文地址目录目录目录深入理解跨平台方案的历史发展逻辑浅述跨平台开发的背景跨平台开发方案的三个时代Web容器时代泛Web容器时代自绘引擎时代我该选择哪一类跨平台......
  • Oracle最高可用性架构(MAA)|青铜级(BRONZE)
    提到数据库高可用,Oracle作为先行者,其体系全面,值得参考和学习1、什么是MAA先下个定义MAA即最高可用性架构(MaximumAvailabilityArchitecture )Oracle最高可用性架构(MAA......
  • Elasticsearch 缓存深入详解
    手敲脑图串讲Elasticsearch核心知识点1、Elasticsearch缓存引出Elasticsearch查询的响应需要占用CPU、内存资源,在复杂业务场景,会出现慢查询,需要花费大量的时间。如......
  • 深入理解JVM(五)-JVM设置参数大全
    标准参数(-)所有的JVM实现都必须实现这些参数的功能,而且向后兼容;命令java-help可以列出java应用启动时标准选项(不同的JVM实现是不同的)。标准参数比较常用的配置参数说明-ve......
  • Oracle 19C静默安装详解
    1.检查主机名和网络并且配置/etc/hosts文件关闭防火墙#检查防火墙状态[root@bogonapp]#systemctlstatusfirewalld.service●firewalld.service-firewalld-dynamic......
  • oracle open hang 等待cursor: pin S wait on X---惜分飞
    客户19.3数据库无法在open过程hang住分析alert日志 2022-10-18T15:04:57.374918+08:00db_recovery_file_dest_sizeof102400MBis9.58%used.Thisisaus......
  • 1-01-RPC框架深入剖析与设计实践(上)_ev
              超时丢弃处理逻辑          优雅关闭代码实现          过载保护用请求队列的长度去控制 ......
  • 记录清理Oracle归档日志
    一、登录数据库1.切换到Oracle用户su命令–切换用户身份su命令来自于英文单词“switchuser”的缩写,其功能是用于切换用户身份。管理员切换至任意用户身份而无需密......