【技术应用】java基于UNIX域套接字(unix domain socket)连接redis

news2024/11/16 7:29:52

【技术应用】java基于UNIX域套接字unix domain socket连接redis

    • 一、前言
    • 二、实现思路
    • 三、代码实现
      • 1、java socket基于redis.sock连接redis
      • 2、Lettuce框架基于redis.sock连接redis

一、前言

在公司工作中经常涉及到一些中小型项目,这些项目都会涉及使用redis数据库,但是程序代码和redis一般都位于同一个服务器上,所以不涉及到跨网络请求,但是java操作redis时,使用的操作redis请求(redis://localhost:6379)往往经过网卡,也影响了性能;
通过学习redis我们了解到,redis是支持UNIX域套接字请求,但是需要配置redis.conf文件,生成redis.sock文件,以便客户端使用;

二、实现思路

1)我们知道所有的clientserver端的数据交互都是通过tcp协议实现的,在java中我们可以使用socket实现,这些操作比较麻烦,命令需要我们自己封装,但是有助于我们更好的理解;
此方案仅供学习参考

redis 客户端和服务端之间通信的协议是RESP(REdis Serialization Protocol)传输层使用TCP

a、请求格式

*3\r\n
$3\r\n
set\r\n
$4\r\n
name\r\n
$8\r\n
zhangsan\r\n
*3 表示下面有3条命令
$3 表示后面3个字符是一个命令,即set
$4 表示后面4个字符是一个命令,即name
$8 表示后面8个字符是一个命令,即zhangsan
所以这个请求格式就表示set name zhangsan命令。

b、响应格式

对于简单字符串,回复的第一个字节是“+”
对于错误,回复的第一个字节是“ - ”
对于整数,回复的第一个字节是“:”
对于批量字符串,回复的第一个字节是“$”
对于数组,回复的第一个字节是“ *

2)使用redisJedisLettuceRedisson框架实现UNIX Domain Socket连接redis,但是博主只通过lettuce框架实现了功能,Jedisredisson没有找到好的实现方式,如果有实现的朋友,可以分享一下;

3)redis服务端设置redis.sock

a、修改redis.conf配置文件

# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
unixsocket /tmp/redis.sock
unixsocketperm 755

# Close the connection after a client is idle for N seconds (0 to disable)

b、redis.sock默认目录
在这里插入图片描述

三、代码实现

1、java socket基于redis.sock连接redis

1)pom.xml文件

<dependency>
            <groupId>com.github.jnr</groupId>
            <artifactId>jnr-unixsocket</artifactId>
            <version>0.38.8</version>
        </dependency>

2)关键代码

关键在于通过UNIX Domain Socket实现socket连接

package com.sk.init;

import jnr.unixsocket.UnixSocket;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


@Slf4j
@Component
public class MyClient03 {

    //定义属性
    //redis.sock路径
    private static final String path="/tmp/redis.sock";

    /**
     * 主函数
     */

    @SneakyThrows
    public void start() {
        //1.建立unixSocket
        File sockFile = new File(path);
        UnixSocketAddress address = new UnixSocketAddress(sockFile);
        UnixSocketChannel channel = UnixSocketChannel.open(address);
        UnixSocket unixSocket = new UnixSocket(channel);

        //2.判断是否连接到服务器
        log.info("是否连接到redis服务器:"+ unixSocket.isConnected());
        //3. 获取iOutputStream输入流,这是发给服务器的请求信息
        @Cleanup  OutputStream outputStream = unixSocket.getOutputStream();
        //4. 获取inputStream输入流,这是服务器返回的响应
        @Cleanup InputStream inputStream = unixSocket.getInputStream();
        //5.开始执行命令
        //(1)执行auth命令
        exec(outputStream,inputStream,"*2\r\n$4\r\nauth\r\n$8\r\n******\r\n");
        //(12)执行ping命令
        exec(outputStream,inputStream,"*1\r\n$4\r\nping\r\n");
        //(3)执行set命令
        exec(outputStream,inputStream,"*3\r\n$3\r\nset\r\n$5\r\ndream\r\n$6\r\nmonkey\r\n");
        //(4)执行get命令
        exec(outputStream,inputStream,"*2\r\n$3\r\nget\r\n$5\r\ndream\r\n");
        //(5)执行del命令
        exec(outputStream,inputStream,"*2\r\n$3\r\ndel\r\n$5\r\ndream\r\n");
        //(6)执行exists命令
        exec(outputStream,inputStream,"*2\r\n$6\r\nexists\r\n$5\r\ndream\r\n");
    }

    private void exec(OutputStream outputStream, InputStream inputStream,String command) throws IOException {
        //1.redis协议字符串 ping
        //String sendInfo="*1\r\n$4\r\nping\r\n";
        //2. 发送授权命令
        outputStream.write(command.getBytes());
        //3.创建字节数组装响应数据
        byte[] responseByte = new byte[1024];
        //4.读取服务器响应到字节数组
        int length = inputStream.read(responseByte);
        //5.将字节数组转换成字符串
        String responseInfo = new String(responseByte, 0, length);
        log.info("执行结果:"+responseInfo);
    }

}

3)执行结果

21:26:36.696 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.start(MyClient03.java:40) - 是否连接到redis服务器:true
21:26:36.710 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:+OK

21:26:36.710 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:+PONG

21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:+OK

21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:$6
monkey

21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果::1

21:26:36.712 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果::0

2、Lettuce框架基于redis.sock连接redis

1)pom.xml依赖

<dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>6.2.1.RELEASE</version>
        </dependency>

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-transport-native-epoll</artifactId>
            <classifier>linux-x86_64</classifier>
        </dependency>

注:必须引入netty-transport-native-epoll依赖,不然Epoll.isAvailable()false,导致程序无法正常执行,具体问题下一篇文章会介绍;

2)关键代码

package com.sk.init;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

@Slf4j
@Component
public class MyClient05 {

    //格式:redis-socket://path[?[timeout=timeout[d|h|m|s|ms|us|ns]][&_database=database_]]
    //完整:redis-socket:///tmp/redis?timeout=10s&_database=0
    private final static String path = "redis-socket:///tmp/redis.sock?timeout=100s&database=0";

    public void start(){

        //创建jedis

        RedisURI uri = RedisURI.create("redis-socket:///tmp/redis.sock");
        uri.setDatabase(0);
        uri.setTimeout(Duration.of(100, ChronoUnit.SECONDS));
        uri.setPassword("******");

        RedisClient redisClient = RedisClient.create(uri);
        StatefulRedisConnection<String, String> connection = redisClient.connect();
        log.info("---------------11111111-------------------");
        connection.setAutoFlushCommands(true);
        RedisCommands<String, String> jedis = connection.sync();
        //jedis.auth("Dsh0406%");
       
        log.info("----------------------------------");

        //执行命令
        String ping = jedis.ping();
        log.info("ping result:{}",ping);
        String set = jedis.set("dream", "girls");
        log.info("set result:{}",set);
        String get = jedis.get("dream");
        log.info("get result:{}",get);
        Long del = jedis.del("dream");
        log.info("del result:{}",del);
        Long exists = jedis.exists("dream");
        log.info("exists result:{}",exists);
    }

}

3)执行结果

20:50:42.453 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:31) - ---------------11111111-------------------
20:50:42.454 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:39) - ----------------------------------
20:50:42.457 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:43) - ping result:PONG
20:50:42.460 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:45) - set result:OK
20:50:42.472 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:47) - get result:girls
20:50:42.474 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:49) - del result:1
20:50:42.476 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient05.start(MyClient05.java:51) - exists result:0

问题:

在实现过程中遇到一个问题,如果redis服务端设置了密码,连接一直报错,提示没有设置密码,但是使用jedis.auth("******")代码设置了密码,但是一直提示密码问题,报错是在redisClient.connect()方法调用上,所以猜测密码应该在RedisClient.create(path)path上设置,
由于UNIX域套节字是在本机提供服务的,也不需要设置密码,所以验证时就把密码取消掉了。

问题解决:
最开始加载redis.sock方式为:

private final static String path = "redis-socket:///tmp/redis.sock?timeout=100s&database=0";
RedisClient redisClient = RedisClient.create(path);

这是Lettuce定制URL连接方式

修改为Lettuce标准的RedisURI连接方式:

RedisURI uri = RedisURI.create("redis-socket:///tmp/redis.sock");
        uri.setDatabase(0);
        uri.setTimeout(Duration.of(100, ChronoUnit.SECONDS));
        uri.setPassword("******");

        RedisClient redisClient = RedisClient.create(uri);

功能还是要多分析多验证

20:43:07.508 [http-nio-8098-exec-1] ERROR org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to /tmp/redis.sock] with root cause
io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
        at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:147) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:116) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:747) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:682) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:599) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.epoll.EpollDomainSocketChannel$EpollDomainUnsafe.epollInReady(EpollDomainSocketChannel.java:138) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.86.Final.jar!/:4.1.86.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.86.Final.jar!/:4.1.86.Final]
        at java.lang.Thread.run(Thread.java:750) ~[?:1.8.0_352]

=如果文章对你有帮助,请点赞、收藏、评论=

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/113931.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis5.0+——持久化——RDBAOF

Redis持久化-RDB 1.实现目标&#xff1a; 在redis持久化时&#xff0c;持久化dump.rdb文件放入到redis解压目录下的data目录下的6379目录下 2.前期准备 1.在redis-5.0.3解压目录下新建data数据目录 2.编辑前面配置的/etc/redis.conf配置文件 修改持久化文件位置 (1) 进入安…

MySQL热备之PXB备份与恢复

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

【语音处理】使用块反射器的基于DFT的系统中用于旁瓣抑制的正交预编码(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Java中的多线程(下)

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaEE 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 多线程案例 单例模式 饿汉模式 懒汉模式 阻塞式队列 为什么要引入阻塞队列 Java中的阻塞队列 模拟实现阻塞队列 定时器 标准库中的定时器 …

docker的虚悬镜像是什么?

虚悬镜像是什么? 答:仓库名、标签都是<none>的镜像,俗称:dangling image 我们使用Dockerfile写一个: 1:编写 from ubuntu CMD echo action is success2:构建 docker build . 注意没有 -t 产生原因: 1:构建时候因为编写错误导致 2:删除的时候 对于这样…

数据溢出的二进制原理

char 类型的数据占一个字节&#xff0c;一个字节有 8 位&#xff0c;最高位为符号位&#xff0c;1表示负数&#xff0c;0表示正数。在计算机中&#xff0c;数据用补码表示&#xff0c;正数的补码是它本身&#xff0c;负数的补码为 “符号位不变&#xff0c;其他位取反后再加1”…

Spring Cloud 系列之OpenFeign:(4)集成OpenFeign

目录 传送门 服务间调用 集成OpenFeign 说明文档 添加pom依赖 启用OpenFeign 声明OpenFeign接口 改造远程调用 定义OpenFeign接口 测试OpenFeign调用 传送门 Spring Cloud Alibaba系列之nacos&#xff1a;(1)安装 Spring Cloud Alibaba系列之nacos&#xff1a;(2)单…

离散数学数理逻辑部分【2】

文章目录命题逻辑等值演算公式的使用【重点】析取范式和合取范式【重点】范式存在定义【了解】求公式A的范式的步骤&#xff1a;【重点】极大项和极小项【重点】主合取范式和主析取范式【重点】等式演算求主析取范式【重点】真值表求主析取范式【了解】主范式的应用【重点】推理…

Python : 使用python实现教务管理系统(GUI界面+数据库)

一、设计目的 1.熟悉Python和相关软件的操作。 2.基于本学期所学Python知识&#xff0c;熟练应用掌握&#xff0c;制作符合要求的教务管理系统。 3.会对程序运行中的错误代码进行分析&#xff0c;找出合理的解决方案。 4.掌握tkinter开发流程&#xff0c;布局方法和主要组件&a…

C语言位域

如果程序的结构中包含多个开关量&#xff0c;只有 TRUE/FALSE 变量&#xff0c;如下&#xff1a; struct {unsigned int widthValidated;unsigned int heightValidated; } status; 这种结构需要 8 字节的内存空间&#xff0c;但在实际上&#xff0c;在每个变量中&#xff0c;…

JavaScript游戏开发(4)(笔记)

文章目录八、角色动作状态的管理8.1 准备部分8.2 角色状态改变的基本方式8.3 完善整个代码8.4 存在的问题九、简单的横板动作卷轴游戏9.1 准备部分9.2 输入管理器9.3 状态管理器9.4 背景管理器9.5 敌人管理器9.6 碰撞检测、UI绘制9.7 更多的角色状态与特效9.8 完善游戏附录素材…

跳槽、换房、不忘输出,与你分享我匆忙的 2022~

前些日子下班回家的瞬间&#xff0c;忽然想起去年春节还在跟老爸吐露职场的困境和对房子的无奈。哪曾想过了不到半年的时间竟全部解决&#xff0c;令我不禁感叹人生的捉摸不透。 让我姑且花点文字记录下&#xff0c;与你分享我这一年的匆匆忙忙&#xff5e; 目录前瞻&#xf…

Listener监听器 | 监听域对象创建和销毁、使用监听器统计网站在线人数

目录 一&#xff1a;监听域对象创建和销毁 1、什么是监听器&#xff1f;监听器有什么用&#xff1f; 2、Servlet规范中提供了哪些监听器&#xff1f; 3、实现一个监听器的步骤 4、HttpSessionBindingListener 5、HttpSessionIdListener & HttpSessionActivationList…

【编译原理】实验二:NFA到DFA

目录 实验二 NFA 到 DFA 一、实验目的 二、预备知识 三、实验内容 NFA向DFA的转换的思路 NFA和DFA之间的联系 NFAToDFA.h 文件 main.c 文件 RegexpToPost.c 文件 PostToNFA.c 文件 NFAFragmentStack.c 文件 PostToNFA.h 文件 NFAFragmentStack.h 文件 NFAStateStack.h 文件 dem…

【C++】使用yaml-cpp操作yaml文件

目录 1 安装yaml-cpp 2 工程结构 &#xff08;1&#xff09;test.yaml的内容 &#xff08;2&#xff09;CmakeLists.txt &#xff08;3&#xff09;代码 3 运行结果 4 报错处理 1 安装yaml-cpp &#xff08;1&#xff09;cd 到yaml-cpp下载的目的路径 例如&#xff1a;…

spring之反射机制之Spring-DI核心实现

文章目录前言一、回顾反射机制之反射调用方法1、编写一个方法类SomeService2、通过反射机制调用SomeService类中的方法二、反射机制之Spring-DI核心实现前言 调用一个方法当中含有几个要素&#xff1f; 1、调用哪个对象 2、调用哪个方法 3、调用方法的时候传什么参数 4、方法执…

一文弄懂 React ref

前言 对于 Ref 理解与使用&#xff0c;一些读者可能还停留在用 ref 获取真实 DOM 元素和获取类组件实例层面上 其实 ref 除了这两项常用功能之外&#xff0c;还有很多别的小技巧 通过本篇文章的学习&#xff0c;你将收获 React ref 的基本和进阶用法&#xff0c;并且能够明白…

LeetCode HOT 100 —— 621. 任务调度器

题目 给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行&#xff0c;并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间&#xff0c;CPU 可以完成一个任务&#xff0c;或者处于待命状态。 然…

使用 SwiftUI 布局协议构建六边形网格,如何制作在六边形网格中显示子视图的通用 SwiftUI 容器

我们将要制作的组件可以作为Swift 包使用。 SwiftUI 非常擅长构建矩形框架的层次结构。随着最近的加入,Grid它变得更好了。然而,今天我们要构建一个疯狂的六边形布局。当然,没有专门的布局类型。所以我们用协议建立我们自己的Layout! 绘制一个六边形 让我们首先为我们的…

在linux中配置redis去中心化集群

目录 前情回顾 一、集群配置 二、启动redis集群 三、检验是否成功 成功&#xff01; 前情回顾 linux中配置redis主从复制及开启哨兵模式 一、集群配置 查看所有的redis服务进程 ps -ef | grep redis 关闭所有的redis服务&#xff08;6379,6380,6381) kill -9 99168 kill …