MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Com

之前没有遇到过MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. 错误信息,但是通过信息应该是磁盘的问题。

确认磁盘空间充足、内存也充足的情况下,网上找了一下解决方案:

有建议设置参数“config set stop-writes-on-bgsave-error no”。但这样子只是暂时性的,问题依旧没有解决。

Linux内核参数之 overcommit_memory

/etc/sysctl.conf
vm.overcommit_memory=1
或者
sysctl vm.overcommit_memory=1
或者
echo 1 > /proc/sys/vm/overcommit_memory

内核参数说明如下:

overcommit_memory文件指定了内核针对内存分配的策略,其值可以是0、1、2。

  •   0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
  • 1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
  • 2, 表示内核允许分配超过所有物理内存和交换空间总和的内存

 

为什么系统明明还剩2GB的内存,Redis会说内存不够呢?

这里有一个帖子 ,分析很到位:http://www.linuxidc.com/Linux/2012-07/66079.htm

 

碰到一个悲催的事情:一台Redis服务器,4核,16G内存且没有任何硬件上的问题。持续高压运行了大约3个月,保存了大约14G的数据,设置了比较完备的Save参数。而就是这台主机,在一次重起之后,丢失了大量的数据,14G的数据最终只恢复了几百兆而已。

正常情况下,像Redis这样定期回写磁盘的内存数据库,丢失几个数据也是在情理之中,可超过80%数据丢失率实在太离谱。排除了误操作的可能性之后,开始寻找原因。

重启动时的日志:

[26641] 21 Dec 09:46:34 * Slave ask for synchronization

[26641] 21 Dec 09:46:34 * Starting BGSAVE for SYNC

[26641] 21 Dec 09:46:34 # Can’t save in background: fork: Cannot allocate memory

[26641] 21 Dec 09:46:34 * Replication failed, can’t BGSAVE

[26641] 21 Dec 09:46:34 # Received SIGTERM, scheduling shutdown…

[26641] 21 Dec 09:46:34 # User requested shutdown…

很明显的一个问题,系统不能在后台保存,fork进程失败。

翻查了几个月的日志,发觉系统在频繁报错:

[26641] 18 Dec 04:02:14 * 1 changes in 900 seconds. Saving…

[26641] 18 Dec 04:02:14 # Can’t save in background: fork: Cannot allocate memory

系统不能在后台保存,fork进程时无法指定内存。

对源码进行跟踪,在src/rdb.c中定位了这个报错:

int rdbSaveBackground(char *filename) {
    pid_t childpid;
    long long start;

    if (server.bgsavechildpid != -1) return REDIS_ERR;
    if (server.vm_enabled) waitEmptyIOJobsQueue();
    server.dirty_before_bgsave = server.dirty;
    start = ustime();
    if ((childpid = fork()) == 0) {
        /* Child */
        if (server.vm_enabled) vmReopenSwapFile();
        if (server.ipfd > 0) close(server.ipfd);
        if (server.sofd > 0) close(server.sofd);
        if (rdbSave(filename) == REDIS_OK) {
            _exit(0);
        } else {
            _exit(1);
        }
    } else {
        /* Parent */
        server.stat_fork_time = ustime()-start;
        if (childpid == -1) {
            redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
                strerror(errno));
            return REDIS_ERR;
        }
        redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);
        server.bgsavechildpid = childpid;
        updateDictResizePolicy();
        return REDIS_OK;
    }
    return REDIS_OK; /* unreached */
}

数据丢失的问题总算搞清楚了!

Redis的数据回写机制分同步和异步两种,

  1. 同步回写即SAVE命令,主进程直接向磁盘回写数据。在数据大的情况下会导致系统假死很长时间,所以一般不是推荐的。
  2. 异步回写即BGSAVE命令,主进程fork后,复制自身并通过这个新的进程回写磁盘,回写结束后新进程自行关闭。由于这样做不需要主进程阻塞,系统不会假死,一般默认会采用这个方法。

个人感觉方法2采用fork主进程的方式很拙劣,但似乎是唯一的方法。内存中的热数据随时可能修改,要在磁盘上保存某个时间的内存镜像必须要冻结。冻结就会导致假死。fork一个新的进程之后等于复制了当时的一个内存镜像,这样主进程上就不需要冻结,只要子进程上操作就可以了。

在小内存的进程上做一个fork,不需要太多资源,但当这个进程的内存空间以G为单位时,fork就成为一件很恐怖的操作。何况在16G内存的主机上fork 14G内存的进程呢?肯定会报内存无法分配的。更可气的是,越是改动频繁的主机上fork也越频繁,fork操作本身的代价恐怕也不会比假死好多少。

找到原因之后,直接修改内核参数vm.overcommit_memory = 1

Linux内核会根据参数vm.overcommit_memory参数的设置决定是否放行。

  1.  如果 vm.overcommit_memory = 1,直接放行
  2. vm.overcommit_memory = 0:则比较 此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上swap,决定是否放行。
  3. vm.overcommit_memory = 2:则会比较 进程所有已分配的虚拟内存加上此次请求分配的虚拟内存和系统当前的空闲物理内存加上swap,决定是否放行。

 

feign调服务错误:feign.RetryableException: Connection refused

2021/01/07 13:37:42.214 ERROR [http-nio-0.0.0.0-7200-exec-35] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/api] threw exception [Request processing failed; nested exception is feign.RetryableException: Connection refused (Connection refused) executing POST http://NJ-DEVICECLOUD-SERVER/njdcd/dispatch] with root cause
java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method) ~[na:1.8.0_171]
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_171]
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_171]
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_171]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_171]
at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_171]
at sun.net.NetworkClient.doConnect(NetworkClient.java:175) ~[na:1.8.0_171]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) ~[na:1.8.0_171]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) ~[na:1.8.0_171]

配置的问题,改成IP就可以了

eureka:
  instance:
    appname: ${spring.application.name}
    virtual-host-name: ${spring.application.name}
    secure-virtual-host-name: ${spring.application.name}
    instance-id: ${server.address2}:${spring.application.name}-peer:${server.port}
    hostname: konke.app.eureka
    #    non-secure-port: 6002 #非安全通信端口
    #    non-secure-port-enabled: true #是否启用非安全端口接受请求
    #    secure-port: 6445 #安全通信端口
    #    secure-port-enabled: true #是否启用安全端口接受请求
    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
    lease-renewal-interval-in-seconds: 30 #eureka节点定时续约时间,默认30
    lease-expiration-duration-in-seconds: 90 #eureka节点剔除时间,默认90
  #    metadata-map:
  #      zone: shanghai

prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false

解决 fatal: refusing to merge unrelated histories

Git 的报错

一、fatal: refusing to merge unrelated histories

新建了一个仓库之后,把本地仓库进行关联提交、拉取的时候,出现了如下错误:

ankobot@DESKTOP-9IUDMKP MINGW64 /e/workspace/firmware_upload/firmware_upload (master)
$ git pull
fatal: refusing to merge unrelated histories

二、解决方案

在你操作命令后面加 –allow-unrelated-histories
例如:

git merge master --allow-unrelated-histories
$ git pull --allow-unrelated-histories
CONFLICT (add/add): Merge conflict in .gitignore
Auto-merging .gitignore
Automatic merge failed; fix conflicts and then commit the result.

我这里由于使用了官方的 .gitignore 自动合并失败,需要手动合并之后再进行 add、commit 即可

如果你是git pull或者git push报fatal: refusing to merge unrelated histories
同理:
git pull origin master –allow-unrelated-histories / git pull –allow-unrelated-histories
等等,就是这样完美的解决!

Spring boot jackson序列化实体类属性大小写问题

查了一下,原来com.fasterxml.jackson搞的鬼,我的问题是实体类有一个属性叫UDID,但是接口返回的JSON变成了小写udid。

使用@JsonProperty(“UDID”)注解可以完美解决调这个问题,例子:

@JsonProperty("UDID")
private String UDID;

放在geter方法同样有效:

@JsonProperty("UDID")
public String getUDID() {
    return UDID;
}

建议是放在geter方法上,如果放在属性上,它会出现两个UDID,一个是大写的,一个小写的。

那么放在方法上呢,就不会出现这个问题。

12325