likes
comments
collection
share

IM系统开发、部署等过程中 遇到的问题记录

作者站长头像
站长
· 阅读数 25

本文是对个人系统:xzll-im 的一篇记录文章,记录我搭建(搭建估计不会记录了现在已经过了这一阶段)、开发、部署im系统以及周边中间件过程中遇到的问题以及解决方式。遇到的问题太多了,时间充足就尽可能记录上,不充足或者问题比较小就在代码中注释记录一下。这文章只给我自己看。以便多年后 回首曾经走过的路😂。仅此而已😄。

  • 此文将不断更新(只要是此系统相关并且我有时间并且我个人觉得有必要)。
  • 由于之前的一些问题都在代码注释中记录了这里就不补充了。以后遇到的较大的、较费时的问题,尽量在这里记录 以便以后统一管理查看以及实现:不被同一块石头绊倒两次。
  • 本文始于:2024-06-25

1、RocketMq发消息报错:the broker's disk is full [CL: 0.95 CQ: 0.95 INDEX: 0.95] 解决

最近将im依赖的各个组件都迁移到了虚拟机的docker上去了,但是发MQ消息时却报错:

org.apache.rocketmq.client.exception.MQBrokerException: CODE: 14  DESC: service not available now. It may be caused by one of the following reasons: the broker's disk is full [CL:  0.95 CQ:  0.95 INDEX:  0.95], messages are put to the slave, message store has been shut down, etc.
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.processSendResponse(MQClientAPIImpl.java:556) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:358) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:340) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:294) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:808) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendSelectImpl(DefaultMQProducerImpl.java:1072) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1044) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1039) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:458) ~[rocketmq-client-4.5.0.jar:4.5.0]
	at com.xzll.connect.cluster.mq.RocketMqProducerWrap.sendClusterEvent(RocketMqProducerWrap.java:80) ~[classes/:?]
	at com.xzll.connect.cluster.provider.C2CMsgProvider.offLineMsg(C2CMsgProvider.java:64) ~[classes/:?]
	at com.xzll.connect.strategy.impl.c2c.C2CMsgSendStrategyImpl.exchange(C2CMsgSendStrategyImpl.java:116) ~[classes/:?]
	at com.xzll.connect.dispatcher.HandlerDispatcher.dispatcher(HandlerDispatcher.java:43) ~[classes/:?]
	at com.xzll.connect.netty.handler.WebSocketServerHandler.lambda$handleWebSocketFrame$2(WebSocketServerHandler.java:211) ~[classes/:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]

说明个前提:

我的rocketMQ是docker启动的,数据会挂载到宿主机中的目录,具体从启动命令可以看出:

# 启动 RocketMQ 的 NameServer
docker run -d \
  --name rmqnamesrv \
  -p 9876:9876 \
  -v /usr/local/soft_hzz/docker/20240624/store:/root/store \
  -v /usr/local/soft_hzz/docker/20240624/logs:/root/logs \
  apache/rocketmq:4.8.0 \
  sh mqnamesrv

# 启动 RocketMQ 的 Broker
docker run -d \
  --name rmqbroker \
  -p 10911:10911 \
  -p 10909:10909 \
  -v /usr/local/soft_hzz/docker/20240624/store:/root/store \
  -v /usr/local/soft_hzz/docker/20240624/logs:/root/logs \
  -v /usr/local/soft_hzz/docker/20240624/conf/broker.conf:/opt/rocketmq-4.8.0/conf/broker.conf \
  apache/rocketmq:4.8.0 \
  sh mqbroker -c /opt/rocketmq-4.8.0/conf/broker.conf

尝试解决:

github上也有类似问题的讨论:github.com/apache/rock… ,其中有人提到权限不够,这确实也是一个可能引发此问题的点,为了打消这个目录读写权限方面的疑点,我接下来将rocketmq挂载的目录权限和容器内的目录的权限都放到最大,以确保docker容器用户可以写入,如下

sudo chown -R $(whoami):$(whoami) /你的rocketmq数据存储路径/store
sudo chown -R $(whoami):$(whoami) /你的rocketmq日志文件存储路径/logs
sudo chmod -R 777 /你的rocketmq数据存储路径/store
sudo chmod -R 777 /你的rocketmq日志文件存储路径/logs

之后我们可以看到已经是最大权限了: IM系统开发、部署等过程中 遇到的问题记录 赋完权限后��另一个问题就是磁盘不足,情况如下: 可以看到挂载根文件系统的 /dev/mapper/centos-root (是一个逻辑卷管理器(LVM)卷,用于挂载根文件系统(/)。它管理和存储系统的主要数据,包括操作系统、应用程序和配置文件)用了92% ! : IM系统开发、部署等过程中 遇到的问题记录

ok那我就老老实实给虚拟机加磁盘吧,再不加的话虚拟机要罢工了。。。 IM系统开发、部署等过程中 遇到的问题记录 在 VirtualBox 中添加新磁盘后,需要手动将新磁盘的空间分配给现有的分区(注意这一步必须有!),从而让系统可以使用这些新分配的空间,步骤如下:

  1. 首先看下在VirtualBox界面创建完磁盘后目前的情况
lsblk

IM系统开发、部署等过程中 遇到的问题记录

可以看到在界面创建完磁盘后,已经成功添加了一个新的 20GB 磁盘 (sdb)。接下来要做的是:将这个新的磁盘空间添加到现有的 LVM 卷组中,并扩展根分区 (/dev/centos/root)

  1. 创建一个新的物理卷
sudo pvcreate /dev/sdb
  1. 将新的物理卷添加到现有的卷组
sudo vgextend centos /dev/sdb
  1. 扩展逻辑卷
sudo lvextend -l +100%FREE /dev/centos/root

以上 1、2、3 步截图如下: IM系统开发、部署等过程中 遇到的问题记录 从提示上来看 逻辑卷已经扩展成功,接下来扩展文件系统。

  1. 扩展文件系统
sudo xfs_growfs /

IM系统开发、部署等过程中 遇到的问题记录

  1. 验证,观察是否应用到现有分区
df -h

IM系统开发、部署等过程中 遇到的问题记录

从上可以看到 现在dev/mapper/centos/root 已经变为37GB,使用率为 43% 成功扩容磁盘(后期不够再扩吧)。之后的话我将rocketmq重启。 IM系统开发、部署等过程中 遇到的问题记录

测试一下:可以看到,可以发送消息并正常消费了 IM系统开发、部署等过程中 遇到的问题记录 IM系统开发、部署等过程中 遇到的问题记录

2、使用redisTemplate 的execute执行lua脚本后,hash的key或value只要是string类型都被加上了 双引号 的解决记录

最近使用lua 将几个非原子命令搞成原子的,但是却发现 存进redis 的数据不管是key 还是value只要类型是string就会无缘无故多了个 双引号。如下(value没有是因为是int类型 如果是string也会加双引号): IM系统开发、部署等过程中 遇到的问题记录 而我获取时 必须得这样获取才能获取到: IM系统开发、部署等过程中 遇到的问题记录 而这不是我想要的,我存啥就给我放啥得了呗 别自作主张给我加,于是我一通排查,最开始我以为redisTemplate序列化有问题,但是最后发现其实并不是,如下是我的序列化配置,

@Bean("redisTemplate")
@Primary
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);

    //设置key 序列化的方式
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringRedisSerializer);

    //设置value 序列化的方式
    Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
    serializer.setObjectMapper(objectMapper);

    redisTemplate.setValueSerializer(serializer);

    //设置 hash key value 序列化的方式
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(new StringRedisSerializer());

    // 禁用默认序列化器
    redisTemplate.setEnableDefaultSerializer(false);
    redisTemplate.setDefaultSerializer(null);

    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

后来发现:redisTemplate的execute方法重载了好几个,其中有这么个重载方法: IM系统开发、部署等过程中 遇到的问题记录 从参数就知道可以指定args和反参的序列化类型,嘿那不正好,来试试吧! IM系统开发、部署等过程中 遇到的问题记录

启动项目以及客户端,发现redis中的数据不再带有 双引号了,如下所示: IM系统开发、部署等过程中 遇到的问题记录 代码中也可以按期望 获取成功了。 IM系统开发、部署等过程中 遇到的问题记录

3、宿主机连不上虚拟机中docker部署的服务(如nacos)

在家里,使用docker 桥接网络模式(默认的) 启动nacos 死活连不上,在虚拟机启动了rocketmq和mysql 在宿主机进行 telnet 192.168.1.103 9876 或者 telnet 192.168.1.103 3306 或者相互ping ip ,都没问题,说明宿主机和虚拟机直接网络没问题,那大概率就是docker0 这个网卡问题,在没找到原因之前,只能改成host模式启动docker 容器了,也即:让docker容器 使用虚拟机的端口。不进行虚拟机到docker0网卡的端口转发了。使用host模式的话只需要在启动时指定network即--network host,如下这个就是使用的host模式:

sudo docker run -d --name nacos-home-host --network host -e MODE=standalone -p 8848:8848 nacos/nacos-server:2.0.3

后续解决后补充:

我发现问题出在docker0网卡的设置上,即:之前一直宿主机连接不上虚拟机中docker内的服务(比如nacos) 可能是因为虚拟机和docker0的网段冲突,docker0网卡的网段也是192.168.1.xx,虚拟机是192.169.1.103, 我将/etc/docker/daemon.json 文件(就是你配镜像的那个文件)追加 :"bip": "172.17.0.1/24"(这个是docker0网卡默认的ip) 后 执行:

停止所有docker 容器

sudo systemctl stop docker

重启后 即自动创建docker0 网卡

sudo systemctl restart docker

然后再ifconfig 发现docker0网卡变为172.17.0.1, 之后再重启 docker restart nacos 就可以了,what fuck ! 倒腾很久,哎。。。

贴上启动nacos的命令:

sudo docker run -d --name nacos -e MODE=standalone -p 8848:8848 nacos/nacos-server:2.0.3

能被这个问题拌住,说明对docker运行机制还不熟 尤其是他的几种网络模式。关于docker的几种模式这篇文章感觉讲的不错很清晰:www.cnblogs.com/liugp/p/163…

4、dubbo指定网卡注册问题记录与多种方式解决

最近将本地mac上的服务,搞到了虚拟机中启动,但是在dubbo消费的时候,报了一个错: com.xzll.connect.rpcapi.RpcSendMsg2ClientApi from registry 172.30.128.65:2181 on the consumer 192.168.1.1 using the dubbo version 3.0.6. Please check if the providers have been started and registered

org.apache.dubbo.rpc.RpcException: Failed to invoke the method responseClientAck2Client in the service com.xzll.connect.rpcapi.RpcSendMsg2ClientApi. No provider available for the service com.xzll.connect.rpcapi.RpcSendMsg2ClientApi from registry 172.30.128.65:2181 on the consumer 192.168.1.1 using the dubbo version 3.0.6. Please check if the providers have been started and registered.
	at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.checkInvokers(AbstractClusterInvoker.java:366) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:59) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:340) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.router.RouterSnapshotFilter.invoke(RouterSnapshotFilter.java:46) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:321) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:99) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:321) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:51) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:321) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter.invoke(ConsumerContextFilter.java:108) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CopyOfFilterChainNode.invoke(FilterChainBuilder.java:321) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder$CallbackRegistrationInvoker.invoke(FilterChainBuilder.java:193) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster$ClusterFilterInvoker.invoke(AbstractCluster.java:92) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:97) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.registry.client.migration.MigrationInvoker.invoke(MigrationInvoker.java:280) ~[dubbo-3.0.6.jar!/:3.0.6]
	at org.apache.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:97) ~[dubbo-3.0.6.jar!/:3.0.6]
	at com.xzll.connect.rpcapi.RpcSendMsg2ClientApiDubboProxy0.responseClientAck2Client(RpcSendMsg2ClientApiDubboProxy0.java) ~[im-connect-api-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
	at jdk.internal.reflect.GeneratedMethodAccessor143.invoke(Unknown Source) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at com.sun.proxy.$Proxy111.responseClientAck2Client(Unknown Source) ~[?:?]
	at com.xzll.business.handler.c2c.C2CClientReceivedAckMsgHandler.clientReceivedAckMsgDeal(C2CClientReceivedAckMsgHandler.java:64) ~[classes!/:0.0.1-SNAPSHOT]
	at com.xzll.business.handler.c2c.C2CClientReceivedAckMsgHandler$$FastClassBySpringCGLIB$$113371f.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.20.jar!/:5.3.20]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.20.jar!/:5.3.20]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
	at com.xzll.business.handler.c2c.C2CClientReceivedAckMsgHandler$$EnhancerBySpringCGLIB$$a22b9c6b.clientReceivedAckMsgDeal(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
	at com.xzll.business.cluster.consumer.C2CMsgEventConsumer.handleEvent(C2CMsgEventConsumer.java:81) ~[classes!/:0.0.1-SNAPSHOT]
	at com.xzll.common.rocketmq.ClusterConsumerEventHandler.orderMessageHandle(ClusterConsumerEventHandler.java:78) ~[im-common-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
	at com.xzll.common.rocketmq.RocketMQOrderConsumerListener.consumeMessage(RocketMQOrderConsumerListener.java:37) ~[im-common-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
	at org.apache.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService$ConsumeRequest.run(ConsumeMessageOrderlyService.java:471) ~[rocketmq-client-4.5.0.jar!/:4.5.0]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]

? 我的虚拟机ip是172.30.128.65呀 怎么注册到dubbo上的是192.168.1.1呢? 奇怪。注册情况如下: IM系统开发、部署等过程中 遇到的问题记录

192.168.1.1这个ip正好是我的docker0网卡ip: IM系统开发、部署等过程中 遇到的问题记录 而我 不希望 dubbo使用这个网卡的ip来注册到zk,而是希望 主网卡enp0s3 的ip注册到zk上去。怎么办呢?

一通排查搜索发现 这个和dubbo注册时的网卡选择策略有关,dubbo选网卡代码如下:


类是:org.apache.dubbo.common.utils.NetUtils


private static InetAddress getLocalAddress0() {
    InetAddress localAddress = null;

    try {
        NetworkInterface networkInterface = findNetworkInterface();
        Enumeration addresses = networkInterface.getInetAddresses();

        while(addresses.hasMoreElements()) {
            Optional<InetAddress> addressOp = toValidAddress((InetAddress)addresses.nextElement());
            if (addressOp.isPresent()) {
                try {
                    if (((InetAddress)addressOp.get()).isReachable(100)) {
                        return (InetAddress)addressOp.get();
                    }
                } catch (IOException var6) {
                }
            }
        }
    } catch (Throwable var7) {
        logger.warn(var7);
    }

    try {
        localAddress = InetAddress.getLocalHost();
        Optional<InetAddress> addressOp = toValidAddress(localAddress);
        if (addressOp.isPresent()) {
            return (InetAddress)addressOp.get();
        }
    } catch (Throwable var5) {
        logger.warn(var5);
    }

    return localAddress;
}


public static NetworkInterface findNetworkInterface() {
    List validNetworkInterfaces = Collections.emptyList();

    try {
        //获取所有有效的网卡
        validNetworkInterfaces = getValidNetworkInterfaces();
    } catch (Throwable var8) {
        logger.warn(var8);
    }

    NetworkInterface result = null;
    Iterator var2 = validNetworkInterfaces.iterator();

    NetworkInterface networkInterface;
    while(var2.hasNext()) {
        networkInterface = (NetworkInterface)var2.next();
        //这一步会将 优先选择 配置了优先的网卡!
        if (isPreferredNetworkInterface(networkInterface)) {
            result = networkInterface;
            break;
        }
    }

    if (result == null) {
        var2 = validNetworkInterfaces.iterator();

        while(var2.hasNext()) {
            networkInterface = (NetworkInterface)var2.next();
            Enumeration addresses = networkInterface.getInetAddresses();

            while(addresses.hasMoreElements()) {
                Optional<InetAddress> addressOp = toValidAddress((InetAddress)addresses.nextElement());
                if (addressOp.isPresent()) {
                    try {
                        if (((InetAddress)addressOp.get()).isReachable(100)) {
                            return networkInterface;
                        }
                    } catch (IOException var7) {
                    }
                }
            }
        }
    }

    if (result == null) {
        result = (NetworkInterface)CollectionUtils.first(validNetworkInterfaces);
    }

    return result;
}


private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
    List<NetworkInterface> validNetworkInterfaces = new LinkedList();
    Enumeration interfaces = NetworkInterface.getNetworkInterfaces();

    while(interfaces.hasMoreElements()) {
        NetworkInterface networkInterface = (NetworkInterface)interfaces.nextElement();
        //这一步会将 忽略的网卡 过滤掉!
        if (!ignoreNetworkInterface(networkInterface)) {
            validNetworkInterfaces.add(networkInterface);
        }
    }

    return validNetworkInterfaces;
}

以上代码归纳总结一下大概就是这么几件事:

  1. 查找所有合适的网卡
  2. 如果没找到网卡,则设置ip为 127.0.0.1
  3. 找到了则校验网卡对应的ip是否有效(会在这一步 过滤掉dubbo.network.interface.ignored配置的网卡)
  4. 在查找的网卡里遍历一遍,看是否有优先设置的(对应 dubbo.network.interface.preferred配置)
  5. 如果没有配置优先的网卡,选择一个网速小于100毫秒响应的网卡
  6. 如果还没有,则返回第一个网卡
  7. 最终将胜出的网卡 对应的ip注册到zk中去

而我现在是没有配置优先或者忽略的。想必dubbo这个工具类的逻辑大概如这样: 找到的网卡列表中 docker0是第一个元素 并且他也在100毫秒响应了,所以选择了这个docker0网卡对应的192.168.1.1 这个ip作为注册到zk上的ip

而我想设置enp0s3这个网卡的ip为注册ip (不卖关子直接给出答案) ,很简单在启动时设置系统参数,也就是说告诉dubbo你要优先哪个 过滤哪个。先看下dubbo中获取配置的代码:

//获取忽略的网卡
String ignoredInterfaces = System.getProperty("dubbo.network.interface.ignored");
//获取优先的网卡
String ignoredInterfaces = System.getProperty("dubbo.network.interface.preferred");

知道了dubbo怎么读的了,那么我们就在启动的时候指定,不就完事了? 所以接下来,直接在启动项目时 指定系统参数:由原来的

java -jar im-connect-service.jar --server.port=8087
java -jar im-business-service.jar --server.port=8097

改为:

java -Ddubbo.network.interface.preferred=enp0s3 -Ddubbo.network.interface.ignored=docker0 -jar im-connect-service.jar --server.port=8087
java -Ddubbo.network.interface.preferred=enp0s3 -Ddubbo.network.interface.ignored=docker0 -jar im-business-service.jar --server.port=8097

ok 大功告成,实测是可行的。如下,成功将enp0s3的网卡的ip注册到zk: IM系统开发、部署等过程中 遇到的问题记录

ok到此就可以了,但是如果你就是不想在启动时加这个参数,而是想要将其配到application.yaml配置中心的文件中去,那么该如何做呢?

步骤简述:

  • 首先在application.yaml中配置: IM系统开发、部署等过程中 遇到的问题记录
  • 然后编写代码(我选择实现ApplicationContextInitializerinitialize方法 这个方法的执行时机 是spring容器还没被初始化之前 具体可以看我之前的一篇关于spring扩展点的文章,) IM系统开发、部署等过程中 遇到的问题记录
    • (注意 一定要在dubbo读取之前,从application.ymal读取配置并设置进去系统环境中,否则不生效,说白了就是让):IM系统开发、部署等过程中 遇到的问题记录这一步尽量提前。保证其在dubbo读取的前边执行。
    • 之后我们让IM系统开发、部署等过程中 遇到的问题记录生效
  • 之后打包上传到虚拟机并启动: IM系统开发、部署等过程中 遇到的问题记录
  • 然后看下zk里: IM系统开发、部署等过程中 遇到的问题记录
  • 看下调用情况 IM系统开发、部署等过程中 遇到的问题记录

ok到此问题就解决了。当然 还有多种方式指定dubbo选址的方式 比如:

  1. 可以配置 /etc/hosts 文件,将 hostname 对应的 IP 显式配置进去。
  2. 可以使用启动参数去显式指定注册的 IP:
-DDUBBO_IP_TO_REGISTRY=1.2.3.4

也可以指定 Dubbo 服务绑定在哪块网卡上:

-DDUBBO_IP_TO_BIND=1.2.3.4

或者指定dubbo的注册ip: IM系统开发、部署等过程中 遇到的问题记录

推荐看一看,一篇dubbo官网上质量很高的文章:研究-dubbo-网卡地址注册时的一点思考

5、集成 shardingsphere 过程中遇到的一些问题

snakeyaml版本问题

IM系统开发、部署等过程中 遇到的问题记录

具体错误信息:

***************************
APPLICATION FAILED TO START
***************************

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.<init>(ShardingSphereYamlConstructor.java:44)

The following method did not exist:

    'void org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(int)'

The calling method's class, org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1, was loaded from the following location:

    jar:file:/Users/hzz/.m2/repository/org/apache/shardingsphere/shardingsphere-infra-util/5.2.1/shardingsphere-infra-util-5.2.1.jar!/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor$1.class

The called method's class, org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1, is available from the following locations:

    jar:file:/Users/hzz/.m2/repository/org/apache/shardingsphere/shardingsphere-infra-util/5.2.1/shardingsphere-infra-util-5.2.1.jar!/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor$1.class

The called method's class hierarchy was loaded from the following locations:

    null: file:/Users/hzz/.m2/repository/org/apache/shardingsphere/shardingsphere-infra-util/5.2.1/shardingsphere-infra-util-5.2.1.jar
    org.yaml.snakeyaml.LoaderOptions: file:/Users/hzz/.m2/repository/org/yaml/snakeyaml/1.30/snakeyaml-1.30.jar


Action:

Correct the classpath of your application so that it contains a single, compatible version of org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1

网上搜索了下,解决办法为升级snakeyaml的版本,这里我直接显示指定:

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.33</version>
</dependency>

之后刷新maven重启服务则没有问题了。

无法确定分片的问题

IM系统开发、部署等过程中 遇到的问题记录

报错详情:

org.springframework.jdbc.UncategorizedSQLException: 
### Error updating database.  Cause: java.sql.SQLException: Unknown exception: Insert statement does not support sharding table routing to multiple data nodes.
### The error may exist in com/xzll/business/mapper/ImC2CMsgRecordMapper.java (best guess)
### The error may involve com.xzll.business.mapper.ImC2CMsgRecordMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO im_c2c_msg_record  ( from_user_id, to_user_id,   msg_content, msg_id, msg_create_time, msg_format, msg_type, msg_status,   chat_id )  VALUES  ( ?, ?,   ?, ?, ?, ?, ?, ?,   ? )
### Cause: java.sql.SQLException: Unknown exception: Insert statement does not support sharding table routing to multiple data nodes.
; uncategorized SQLException; SQL state [HY000]; error code [30000]; Unknown exception: Insert statement does not support sharding table routing to multiple data nodes.; nested exception is java.sql.SQLException: Unknown exception: Insert statement does not support sharding table routing to multiple data nodes.
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92) ~[mybatis-spring-2.0.6.jar:2.0.6]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441) ~[mybatis-spring-2.0.6.jar:2.0.6]
	at com.sun.proxy.$Proxy147.insert(Unknown Source) ~[?:?]
	at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:272) ~[mybatis-spring-2.0.6.jar:2.0.6]
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:59) ~[mybatis-plus-core-3.5.0.jar:3.5.0]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148) ~[mybatis-plus-core-3.5.0.jar:3.5.0]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) ~[mybatis-plus-core-3.5.0.jar:3.5.0]
	at com.sun.proxy.$Proxy151.insert(Unknown Source) ~[?:?]
	at com.xzll.business.service.impl.ImC2CMsgRecordServiceImpl.saveC2CMsg(ImC2CMsgRecordServiceImpl.java:64) ~[classes/:?]
	at com.xzll.business.service.impl.ImC2CMsgRecordServiceImpl$$FastClassBySpringCGLIB$$1bc9006d.invoke(<generated>) ~[classes/:?]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar:5.3.20]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar:5.3.20]

解决办法: 仔细检查你的分片配置,最好是按官网来,不要从别处copy,我在这个上边走了些弯路,后来按照官方文档改好了。最终正确的可按照分库分表规则读写数据的写法如下(后期可能读写分离等等,这算是第一版分库分表配置):


spring:
  shardingsphere:
    mode:
      # 暂时单机模式
      type: Standalone
    # 没有进行分库分表的表,走这个数据源
    default-data-source-name: ds0
    props:
      sql:
        show: true
    datasource:
      # 定义数据源名称
      names: ds0, ds1
      # 数据源1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://172.30.128.66:3306/xzll_im_db_0?useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
        username: root
        password: xzllaigH95..
      # 数据源2
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://172.30.128.67:3306/xzll_im_db_1?useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
        username: root
        password: xzllaigH95..
    rules: 
      sharding:
        tables:
          # 对消息记录表(im_c2c_msg_record)进行分库分表(根据会话id分为两个库,每个库10张表)
          im_c2c_msg_record:
            actual-data-nodes: ds${0..1}.im_c2c_msg_record_${0..9}
            # 分表策略 ,分片策略由 (分片键+分片算法) 组成 !!!
            table-strategy:
              standard:
                # 分片键
                sharding-column: msg_id
                # 分片策略
                sharding-algorithm-name: im_c2c_msg_record_limit_table_algorithm
            # 分库策略    
            database-strategy:       
              standard:
                sharding-column: chat_id
                sharding-algorithm-name: im_c2c_msg_record_limit_db_algorithm
            keyGenerator:
              column: id
              # 分库分表后,id自增的话将会重复,所以这里最好是全局唯一的,比如雪花算法或者uuid等等
              type: SNOWFLAKE
        # 分片算法配置
        sharding-algorithms:
          # 消息记录表(水平分表的算法)
          im_c2c_msg_record_limit_table_algorithm: 
            # 分片算法类型
            type: INLINE 
            props:
              algorithm-expression: im_c2c_msg_record_${Math.abs(msg_id.hashCode()) % 10} 
          # 消息记录表(水平分库的算法)
          im_c2c_msg_record_limit_db_algorithm: 
            type: INLINE 
            props:
              algorithm-expression: ds${Math.abs(chat_id.hashCode()) % 2} 

分片时计算出的 hash 值为负数

如下,因为我是用消息id来分片的,而消息id是个字符串类型,所以导致在计算hash值时 会出现负数,从而算出的分片物理表是 im-c2c_msg_record_-3 这种的,那肯定不对呀 , 所以给他取个绝对值,就解决了,这个问题比较简单。 IM系统开发、部署等过程中 遇到的问题记录 取绝对值后 完美解决: IM系统开发、部署等过程中 遇到的问题记录

转载自:https://juejin.cn/post/7383982728734588965
评论
请登录