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

2023-11-05 14:51

本文主要是介绍【技术应用】java基于UNIX域套接字(unix domain socket)连接redis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【技术应用】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";/*** 主函数*/@SneakyThrowspublic void start() {//1.建立unixSocketFile 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) - 执行结果:+OK21:26:36.710 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:+PONG21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:+OK21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果:$6
monkey21:26:36.711 [http-nio-8098-exec-1] INFO  com.sk.init.MyClient03.exec(MyClient03.java:71) - 执行结果::121: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=0private final static String path = "redis-socket:///tmp/redis.sock?timeout=100s&database=0";public void start(){//创建jedisRedisURI 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]

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

这篇关于【技术应用】java基于UNIX域套接字(unix domain socket)连接redis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/350590

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,