第4章 客户端-Java客户端Jedis

1.获取Jedis
maven配置加入项目中

<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.8.2</version>
</dependency>

2.Jedis的基本使用
Jedis的使用方法非常简单,只要下面三行代码就可以实现get功能:

import redis.clients.jedis.Jedis;

public class JedisTest {
    public static void main(String[] args) {
        Jedis jedis=null;
        try {
            //1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通信
             jedis = new Jedis("59.110.35.177", 6379);
            jedis.auth("password");
            // 1.string
            // 输出结果:OK
            jedis.set("hello", "world");
            // 输出结果:world
            jedis.get("hello");
            // 输出结果:1
            jedis.incr("counter");
            // 2.hash
            jedis.hset("myhash", "f1", "v1");
            jedis.hset("myhash", "f2", "v2");
            // 输出结果:{f1=v1, f2=v2}
            jedis.hgetAll("myhash");
            // 3.list
            jedis.rpush("mylist", "1");
            jedis.rpush("mylist", "2");
            jedis.rpush("mylist", "3");
            // 输出结果:[1, 2, 3]
            jedis.lrange("mylist", 0, -1);
            // 4.set
            jedis.sadd("myset", "a");
            jedis.sadd("myset", "b");
            jedis.sadd("myset", "a");
            // 输出结果:[b, a]
            jedis.smembers("myset");
            // 5.zset
            jedis.zadd("myzset", 99, "tom");
            jedis.zadd("myzset", 66, "peter");
            jedis.zadd("myzset", 33, "james");
            // 输出结果:[[["james"],33.0], [["peter"],66.0], [["tom"],99.0]]
            jedis.zrangeWithScores("myzset", 0, -1);
        }
        catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

参数除了可以是字符串,Jedis还提供了字节数组的参数,例如:

public String set(final String key, String value)
public String set(final byte[] key, final byte[] value)
public byte[] get(final byte[] key)
public String get(final String key)

有了这些API的支持,就可以将Java对象序列化为二进制,当应用需要获取Java对象时,使用get(final byte[]key)函数将字节数组取出,然后反序列化为Java对象即可。和很多NoSQL数据库(例如Memcache、Ehcache)的客户端不同,Jedis本身没有提供序列化的工具,也就是说开发者需要自己引入序列化的工具。序列化的工具有很多,例如XML、Json、谷歌的Protobuf、Facebook的Thrift等等,对于序列化工具的选择开发者可以根据自身需求决定,下面以protostuff(Protobuf的Java客户端)为例子进行说明。
1)protostuff的Maven依赖:

<protostuff.version>1.0.11</protostuff.version>
<dependency>
	<groupId>com.dyuproject.protostuff</groupId>
	<artifactId>protostuff-runtime</artifactId>
	<version>${protostuff.version}</version>
</dependency>
<dependency>
	<groupId>com.dyuproject.protostuff</groupId>
	<artifactId>protostuff-core</artifactId>
	<version>${protostuff.version}</version>
</dependency>

2)定义实体类:

// 俱乐部
public class Club implements Serializable {
private int id; // id
private String name; // 名称
private String info; // 描述
private Date createDate; // 创建日期
private int rank; // 排名
// 相应的getter setter不占用篇幅
}

3)序列化工具类ProtostuffSerializer提供了序列化和反序列化方法:

package com.sohu.tv.serializer;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import java.util.concurrent.ConcurrentHashMap;
//序列化工具
public class ProtostuffSerializer {
	private Schema<Club> schema = RuntimeSchema.createFrom(Club.class);
	public byte[] serialize(final Club club) {
		final LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try {
			return serializeInternal(club, schema, buffer);
		} catch (final Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			buffer.clear();
		}
	}
	public Club deserialize(final byte[] bytes) {
		try {
			Club club = deserializeInternal(bytes, schema.newMessage(), schema);
			if (club != null ) {
				return club;
			}
		} catch (final Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
		return null;
	}
	private <T> byte[] serializeInternal(final T source, final Schema<T>schema, final LinkedBuffer buffer) {
		return ProtostuffIOUtil.toByteArray(source, schema, buffer);
	}
	private <T> T deserializeInternal(final byte[] bytes, final T result, finalSchema<T> schema) {
		ProtostuffIOUtil.mergeFrom(bytes, result, schema);
		return result;
		}
}

4)测试。
生成序列化工具类:

ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();

生成Jedis对象:

Jedis jedis = new Jedis("127.0.0.1", 6379);

序列化:

String key = "club:1";
// 定义实体对象
Club club = new Club(1, "AC", "米兰", new Date(), 1);
// 序列化
byte[] clubBtyes = protostuffSerializer.serialize(club);
jedis.set(key.getBytes(), clubBtyes);

反序列化:

byte[] resultBtyes = jedis.get(key.getBytes());
// 反序列化[id=1, clubName=AC, clubInfo=米兰, createDate=Tue Sep 15 09:53:18 CST 2015, rank=1]
Club resultClub = protostuffSerializer.deserialize(resultBtyes);

总体代码为:

import redis.clients.jedis.Jedis;
import java.util.Date;
public class redisSerializerTest {
    public static void main(String[] args) {
        ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();
        Jedis jedis=null;
        try {
            //1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通信
            jedis = new Jedis("59.110.35.177", 6379);
            jedis.auth("1.tiantian");
            String key = "club:1";
            // 定义实体对象
            Club club = new Club(1, "AC", "米兰", new Date(), 1);
            // 序列化
            byte[] clubBtyes = protostuffSerializer.serialize(club);
            jedis.set(key.getBytes(), clubBtyes);
            // 反序列化[id=1, clubName=AC, clubInfo=米兰, createDate=Tue Sep 15 09:53:18 CST 2015, rank=1]
            byte[] resultBtyes = jedis.get(key.getBytes());
            Club resultClub = protostuffSerializer.deserialize(resultBtyes);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

3.Jedis连接池使用
以上是Jedis的直连方式,所谓直连是指Jedis每次都会新建TCP连接,使用后再断开连接,对于频繁访问Redis的场景显然不是高效的使用方式,如下图所示
在这里插入图片描述
因此生产环境中一般使用连接池的方式对Jedis连接进行管理,如下图所示,所有Jedis对象预先放在池子中(JedisPool),每次要连接Redis,只需要在池子中借,用完了在归还给池子。
在这里插入图片描述
客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连接,而连接池的方式是可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。另外直连的方式无法限制Jedis对象的个数,在极端情况下可能会造成连接泄露,而连接池的形式可以有效的保护和控制资源的使用。但是直连的方式也并不是一无是处,表4-1给出两种方式各自的优劣势。
在这里插入图片描述
Jedis提供了JedisPool这个类作为对Jedis的连接池,同时使用了Apache的通用对象池工具common-pool作为资源的管理工具,下面是使用JedisPool操作Redis的代码示例:
1)Jedis连接池(通常JedisPool是单例的):

// common-pool连接池配置,这里使用默认配置,后面小节会介绍具体配置说明
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// 初始化Jedis连接池
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);

2)获取Jedis对象不再是直接生成一个Jedis对象进行直连,而是从连接池直接获取,代码如下:

Jedis jedis = null;
try {
	// 1. 从连接池获取jedis对象
	jedis = jedisPool.getResource();
	// 2. 执行操作
	jedis.get("hello");
} catch (Exception e) {
	logger.error(e.getMessage(),e);
} finally {
	if (jedis != null) {
	// 如果使用JedisPool,close操作不是关闭连接,代表归还连接池
	jedis.close();
	}
}

这里可以看到在finally中依然是jedis.close()操作,为什么会把连接关闭呢,这不和连接池的原则违背了吗?但实际上Jedis的close()实现方式如下:

public void close() {
	// 使用Jedis连接池
	if (dataSource != null) {
		if (client.isBroken()) {
			this.dataSource.returnBrokenResource(this);
		} else {
			this.dataSource.returnResource(this);
		}
		// 直连
	} else {
		client.close();
	}
}

参数说明:

  • dataSource!=null代表使用的是连接池,所以jedis.close()代表归还连接给连接池,而且Jedis会判断当前连接是否已经断开。
  • dataSource=null代表直连,jedis.close()代表关闭连接。
    前面GenericObjectPoolConfig使用的是默认配置,实际它提供有很多参数,例如池子中最大连接数、最大空闲连接数、最小空闲连接数、连接活性检测,等等,例如下面代码:
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// 设置最大连接数为默认值的5倍
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
// 设置最大空闲连接数为默认值的3倍
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3);
// 设置最小空闲连接数为默认值的2倍
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2);
// 设置开启jmx功能
poolConfig.setJmxEnabled(true);
// 设置连接池没有连接后客户端的最大等待时间(单位为毫秒)
poolConfig.setMaxWaitMillis(3000);

上面几个是GenericObjectPoolConfig几个比较常用的属性,表4-2给出了Generic-ObjectPoolConfig其他属性及其含义解释。
在这里插入图片描述
总体代码为:

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class jedisPoolTest {
    public static void main(String[] args) {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        // 设置最大连接数为默认值的5倍
        poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
        // 设置最大空闲连接数为默认值的3倍
        poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3);
        // 设置最小空闲连接数为默认值的2倍
        poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2);
        // 设置开启jmx功能
        poolConfig.setJmxEnabled(true);
        // 设置连接池没有连接后客户端的最大等待时间(单位为毫秒)
        poolConfig.setMaxWaitMillis(3000);
        // 初始化Jedis连接池
        JedisPool jedisPool = new JedisPool(poolConfig, "59.110.35.177", 6379,200,"1.tiantian");
        Jedis jedis = null;
        try {
        // 1. 从连接池获取jedis对象
            jedis = jedisPool.getResource();
        // 2. 执行操作
         String result = jedis.get("hello");
          System.out.println(result);
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            if (jedis != null) {
            // 如果使用JedisPool,close操作不是关闭连接,代表归还连接池
                jedis.close();
            }
        }
    }
}

4.Jedis中Pipeline使用
Jedis支持Pipeline特性,我们知道Redis提供了mget、mset方法,但是并没有提供mdel方法,如果想实现这个功能,可以借助Pipeline来模拟批量删除,虽然不会像mget和mset那样是一个原子命令,但是在绝大数场景下可以使用。下面代码是mdel删除的实现过程。
这里为了节省篇幅,没有写try catch finally,没有关闭jedis。

public void mdel(List<String> keys) {
	Jedis jedis = new Jedis("127.0.0.1");
	// 1)生成pipeline对象
	Pipeline pipeline = jedis.pipelined();
	// 2)pipeline执行命令,注意此时命令并未真正执行
	for (String key : keys) {
		pipeline.del(key);
	}
	// 3)执行命令
	pipeline.sync();
}

说明如下:

  • 利用jedis对象生成一个pipeline对象,直接可以调用jedis.pipelined()。
  • 将del命令封装到pipeline中,可以调用pipeline.del(String key),这个方法和jedis.del(String key)的写法是完全一致的,只不过此时不会真正的执行命令。
  • 使用pipeline.sync()完成此次pipeline对象的调用。
    除了pipeline.sync(),还可以使用pipeline.syncAndReturnAll()将pipeline的命令进行返回,例如下面代码将set和incr做了一次pipeline操作,并顺序打印了两个命令的结果:
Jedis jedis = new Jedis("127.0.0.1");
Pipeline pipeline = jedis.pipelined();
pipeline.set("hello", "world");
pipeline.incr("counter");
List<Object> resultList = pipeline.syncAndReturnAll();
for (Object object : resultList) {
	System.out.println(object);
}

输出结果为:

OK
1

总体代码为:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.List;
import java.util.Set;
public class JedisPipelineTest {
    public static void main(String[] args) {
        Jedis jedis=null;
        try {
             jedis = new Jedis("59.110.35.177", 6379);
            jedis.auth("1.tiantian");
            Set<String> keys = jedis.keys("*");
            // 1)生成pipeline对象
            Pipeline pipeline = jedis.pipelined();
            // 2)pipeline执行命令,注意此时命令并未真正执行
            for (String key : keys) {
                pipeline.del(key);
            }
            // 3)执行命令
            pipeline.sync();
            pipeline.set("hello", "world");
            pipeline.incr("counter");
            List<Object> resultList = pipeline.syncAndReturnAll();
            for (Object object : resultList) {
                System.out.println(object);
            }
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) {
// 如果使用JedisPool,close操作不是关闭连接,代表归还连接池
                jedis.close();
            }        }
    }
}

5.Jedis的Lua脚本使用
Jedis中执行Lua脚本和redis-cli十分类似,Jedis提供了三个重要的函数实现Lua脚本的执行:

Object eval(String script, int keyCount, String... params)
Object evalsha(String sha1, int keyCount, String... params)
String scriptLoad(String script)

eval函数有三个参数,分别是:

  • script:Lua脚本内容。
  • keyCount:键的个数。
  • params:相关参数KEYS和ARGV。
    以一个最简单的Lua脚本为例子进行说明:
return redis.call('get',KEYS[1])

在redis-cli中执行上面的Lua脚本,方法如下:

127.0.0.1:6379> eval "return redis.call('get',KEYS[1])" 1 hello
"world"

在Jedis中执行,方法如下:

String key = "hello";
String script = "return redis.call('get',KEYS[1])";
Object result = jedis.eval(script, 1, key);
// 打印结果为world
System.out.println(result)

scriptLoad和evalsha函数要一起使用,首先使用scriptLoad将脚本加载到Redis中,代码如下:

String scriptSha = jedis.scriptLoad(script);

evalsha函数用来执行脚本的SHA1校验和,它需要三个参数:

  • scriptSha:脚本的SHA1。
  • keyCount:键的个数。
  • params:相关参数KEYS和ARGV。
    执行效果如下:
Stirng key = "hello";
Object result = jedis.evalsha(scriptSha, 1, key);
// 打印结果为world
System.out.println(result);

总体来说,Jedis的使用还是比较简单的,重点注意以下几点即可:
1)Jedis操作放在try catch finally里更加合理。
2)区分直连和连接池两种实现方式优缺点。
3)jedis.close()方法的两种实现方式。
4)Jedis依赖了common-pool,有关common-pool的参数需要根据不同的使用场景,各不相同,需要具体问题具体分析。
5)如果key和value涉及了字节数组,需要自己选择适合的序列化方法。
总体代码为:

import redis.clients.jedis.Jedis;
public class JedisLuaTest {
    public static void main(String[] args) {
        Jedis jedis=null;
        try {
            //1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通信
            jedis = new Jedis("59.110.35.177", 6379);
            jedis.auth("1.tiantian");
            String key = "hello";
            String script = "return redis.call('get',KEYS[1])";
            Object result = jedis.eval(script, 1, key);
            // 打印结果为world
            System.out.println(result);
            String scriptSha = jedis.scriptLoad(script);
            System.out.println(scriptSha);
            Object result1 = jedis.evalsha(scriptSha, 1, key);
        // 打印结果为world
            System.out.println(result1);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

理解IP地址与域名:访问网站的基石

在互联网的世界里&#xff0c;每一次点击、每一次浏览都伴随着一个神秘的数字串——IP地址&#xff0c;以及一个易于记忆的字符串——域名。对于普通用户而言&#xff0c;这两者可能只是浏览网页时的“幕后英雄”&#xff0c;但实际上&#xff0c;它们构成了我们访问网站的基石…

【EtherCAT】TwinCAT3通过PLC修改SDO数据

目录 1、打开twincat3, 左边PLC右键->添加新项&#xff0c;建立PLC工程 2、->References右键添加库 3、找到Tc2_EtherCAT库&#xff0c;点确定。 4、PLC程序ST语言就可以调用下面的功能块函数 5、PLC编程界面右键->输入助手 1、打开twincat3, 左边PLC右键->添…

element ui form 表单验证

表单验证方法 在el-form元素上总体设置校验规则rules&#xff0c;下面是官方案例 <el-form :model"ruleForm" :rules"rules" ref"ruleForm" label-width"100px" class"demo-ruleForm"><el-form-item label"…

单目标应用:基于吸血水蛭优化器(Blood-Sucking Leech Optimizer,BSLO)的微电网优化(MATLAB代码)

一、微电网模型介绍 微电网多目标优化调度模型简介_vmgpqv-CSDN博客 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、吸血水蛭优化器求解微电网 2.1算法简介 吸血水蛭优化器&#xff08;B…

科技助力行政执法:4G无线网络技术在管理指挥中心的应用

随着科技的飞速发展&#xff0c;4G无线网络技术已经越来越成熟&#xff0c;为行政执法管理带来了前所未有的便利与效率。特别是在管理指挥中心&#xff0c;通过实时观看高清现场画面&#xff0c;执法人员可以随时进行调度指挥&#xff0c;掌握行政执法队伍的全过程&#xff0c;…

2024年03月Python六级真题+答案(中国电子学会 )

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;六级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 一、单选题(共25题&#xff0c;共50分) 1.以下选项中&#xff0c;创建类正确的是&#xff1f;&#xff08; &#xff09; A. class test1…

Java——枚举

1. 概念 枚举是在JDK1.5之后引入的&#xff0c;主要用途是&#xff1a;将一组常量组织起来&#xff0c;在这之前表示一组常量通常使用定义常量的方式&#xff1a; public static final int RED 1; public static final int GREEN 2; public static final int BLACK 3;但是…

ED系列可读可写低频地埋电子标识器(内置SIC279芯片)数据写入说明

ED系列低频地埋电子标识器&#xff08;内置SIC279芯片&#xff09;&#xff0c;即带ID可读可写电子标识器&#xff0c;包含ED1100-ID-RW&#xff0c;ED1500-ID-RW&#xff0c;ED2000-ID-RW&#xff0c;支持按固定格式写数据的地埋电子标识器。 ED系列可读可写低频地埋电子标识…

大猫咪守护LoRA:定制你的大猫私人大猫咪宠物写真合影,某音某书流行款

&#x1f339;大家好&#xff01;我是安琪&#xff01;感谢大家的支持与鼓励。 大猫咪LoRA模型简介 今天应群里同学大猫咪宠物合影写真提议&#xff0c;为大家介绍一款来自作者 沐沐人像合成的主题为大猫咪守护的LoRAl模型&#xff1a;沐沐-大猫咪。这是一款当下在某音、某书…

【5G核心网】5G NWDAF(Network Data Analytics Function)网元功能介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

sql查询CONCAT、group_concat、concat_ws函数用法

SELECT str_code, 1 CONCAT 连接多字段字符串就模糊查询 select CONCAT(dept_name, , dept_code , , parent_id) ,* from sys_dept where CONCAT(dept_name, , dept_code , , parent_id) LIKE %16% 2、语法&#xff1a;group_concat( [distinct] 要连接的字段 [order by …

光模块市场受益于AI热潮同比增长45%,行业前景看好

近日&#xff0c;市场研究机构YOLE Group在最新的市场报告中指出&#xff0c;AI驱动的光模块市场将出现同比45%的增长。预计至2024年&#xff0c;数据通信领域的人工智能光收发器市场将实现高达45%的同比增长&#xff0c;展现出了强大的市场活力和广阔的发展前景。 光收发器市…

Amazon OpenSearch Service 现在支持 JSON Web Token(JWT)身份验证和授权

最近&#xff0c;Amazon OpenSearch 推出了一个新功能&#xff0c;支持 JWT 认证和授权。虽然这个功能在开源的 OpenSearch 中早已存在&#xff0c;但在托管的 Amazon OpenSearch 中的实现一直不够理想。 此前的授权方式 控制台登录 内部数据库&#xff1a;使用基本的用户名…

【分布式系列】分布式锁在 Redis 主从部署中的挑战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

易基因:围产期母体有毒物质暴露对子代血液、大脑和肝脏DNA甲基化的影响 | 研究速递

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 母体在环境化学物质中暴露可能对后代的健康造成不利影响。越来越多的证据支持这些不良效应至少部分受表观遗传修饰调控。目前尚不清楚血液中的表观遗传变化是否反映了大脑皮层或肝脏等目…

【机器学习】K-means++: 一种改进的聚类算法详解

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 K-means: 一种改进的聚类算法详解引言1. K-means算法回顾1.1 基本概念1.2 局限性…

TEMU半托管模式引领跨境电商新风尚

TEMU半托管模式作为2024年的热门话题&#xff0c;正吸引着越来越多卖家的目光。继全托管模式取得巨大成功之后&#xff0c;半托管模式的推出无疑为跨境电商行业注入了新的活力。 在选品方向上&#xff0c;TEMU半托管模式强调商品的聚焦与精选。卖家在选择上架商品时&#xff0c…

数据恢复篇:适用于Windows 的顶级数据恢复软件

适用于Windows的免费和付费的最佳数据恢复软件 **嘿&#xff0c;我要和大家一起泄露所有的测试工具。在评论中留下您的想法和最喜欢的选择&#xff01; 适用于 Windows 的最佳数据恢复软件 1.奇客数据恢复 奇客数据恢复版是Microsoft操作系统的顶级数据恢复软件应用程序之一&a…

智能电表和普通电表有什么区别

智能电表和普通电表在多个方面存在显著的区别&#xff0c;以下是对这些区别的详细分析&#xff1a; 一、功能上的区别 1、电能计量功能&#xff1a; 普通电表&#xff1a;只有电能计量功能&#xff0c;用于记录用户消耗的电量。 智能电表&#xff1a;除了基本的电能计量功能…

ChatTTS源码部署

感谢阅读 默认已完成的操作准备工作下载源码安装依赖下载补丁(报错在运行) 界面展示(discord上有各种补丁&#xff0c;我的加了UI补丁和音色增强)提示词常用&#xff08;这个每个音基本都能生效&#xff09;语调类语速类情感类 默认已完成的操作 python版本>3.9 cuda版本的…