智谱AI-SDK入门实践之Java(保姆级教程)

大家好,我是影子,一名以Java学习为主,其余时间探索AI+编程的程序猿


今天跟大家分享一下智谱AI-SDK的入门用法,话不多说,我们直接开始



一、什么是智谱AI


智谱AI(Zhipu AI)是一家致力于人工智能技术研发和应用的公司。该公司由清华大学背景的团队创立,专注于大

模型技术的研究与推广。智谱AI在人工智能领域取得了显著成就,其发布的自研大模型GLM-4等产品。



二、SDK玩法

(一) 注册账号


进入官网(https://maas.aminer.cn/),注册账号实名后,将会赠送有效期一个月的体验包。




(二) 查看自己的API Key







注意:我们常见的API_KEY和API_SECRET,这里采用了统一为API key,使用 .这个符号进行划分。

举个栗子:yingzix688.编程导航真不错。 (本人微信号:yingzix688,欢迎大家交流探讨)

那么,API_KEY:yingzix688

API_SECRET:编程导航真不错

大家只需要看自己的API key进行分割出来即可。



(三) 查阅官方github


官方github地址:https://github.com/zhipuai/zhipuai-sdk-java-v4


引入依赖

<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.0.0</version>
</dependency>


官方示例代码

package com.zhipu.oapi.demo;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.zhipu.oapi.ClientV4;
import com.zhipu.oapi.Constants;
import com.zhipu.oapi.service.v4.embedding.EmbeddingApiResponse;
import com.zhipu.oapi.service.v4.embedding.EmbeddingRequest;
import com.zhipu.oapi.service.v4.file.FileApiResponse;
import com.zhipu.oapi.service.v4.file.QueryFileApiResponse;
import com.zhipu.oapi.service.v4.file.QueryFilesRequest;
import com.zhipu.oapi.service.v4.fine_turning.*;
import com.zhipu.oapi.service.v4.image.CreateImageRequest;
import com.zhipu.oapi.service.v4.image.ImageApiResponse;
import com.zhipu.oapi.service.v4.model.*;
import io.reactivex.Flowable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;


public class V4OkHttpClientTest {

private static final String API_KEY = "";

private static final String API_SECRET = "";

private static final ClientV4 client = new ClientV4.Builder(API_KEY,API_SECRET).build();

private static final ObjectMapper mapper = defaultObjectMapper();


public static ObjectMapper defaultObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
mapper.addMixIn(ChatFunction.class, ChatFunctionMixIn.class);
mapper.addMixIn(ChatCompletionRequest.class, ChatCompletionRequestMixIn.class);
mapper.addMixIn(ChatFunctionCall.class, ChatFunctionCallMixIn.class);
return mapper;
}

// 请自定义自己的业务id
private static final String requestIdTemplate = "mycompany-%d";

public static void main(String[] args) throws Exception {
System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
// 1. sse-invoke调用模型,使用标准Listener,直接返回结果
testSseInvoke();

// 2. invoke调用模型,直接返回结果
// testInvoke();

// 3. 异步调用
// String taskId = testAsyncInvoke();
// 4.异步查询
// testQueryResult(taskId);

// 5.文生图
// testCreateImage();

// 6. 图生文
// testImageToWord();

// 7. 向量模型
// testEmbeddings();

// 8.微调-上传微调数据集
// testUploadFile();

// 9.微调-查询上传文件列表
// testQueryUploadFileList();

// 10.微调-创建微调任务
// testCreateFineTuningJob();

// 11.微调-查询微调任务事件
// testQueryFineTuningJobsEvents();

// 12.微调-查询微调任务
// testRetrieveFineTuningJobs();

// 13.微调-查询个人微调任务
// testQueryPersonalFineTuningJobs();

// 14.微调-调用微调模型(参考模型调用接口,并替换成要调用模型的编码model)
}

private static void testQueryPersonalFineTuningJobs() {
QueryPersonalFineTuningJobRequest queryPersonalFineTuningJobRequest = new QueryPersonalFineTuningJobRequest();
queryPersonalFineTuningJobRequest.setLimit(1);
QueryPersonalFineTuningJobApiResponse queryPersonalFineTuningJobApiResponse = client.queryPersonalFineTuningJobs(queryPersonalFineTuningJobRequest);
System.out.println("model output:" + JSON.toJSONString(queryPersonalFineTuningJobApiResponse));

}

private static void testQueryFineTuningJobsEvents() {
QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest();
queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb");
// queryFineTuningJobRequest.setLimit(1);
// queryFineTuningJobRequest.setAfter("1");
QueryFineTuningEventApiResponse queryFineTuningEventApiResponse = client.queryFineTuningJobsEvents(queryFineTuningJobRequest);
System.out.println("model output:" + JSON.toJSONString(queryFineTuningEventApiResponse));
}

/**
* 查询微调任务
*/
private static void testRetrieveFineTuningJobs() {
QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest();
queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb");
// queryFineTuningJobRequest.setLimit(1);
// queryFineTuningJobRequest.setAfter("1");
QueryFineTuningJobApiResponse queryFineTuningJobApiResponse = client.retrieveFineTuningJobs(queryFineTuningJobRequest);
System.out.println("model output:" + JSON.toJSONString(queryFineTuningJobApiResponse));
}

/**
* 创建微调任务
*/
private static void testCreateFineTuningJob() {
FineTuningJobRequest request = new FineTuningJobRequest();
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
request.setRequestId(requestId);
request.setModel("chatglm3-6b");
request.setTraining_file("file-20240118082608327-kp8qr");
CreateFineTuningJobApiResponse createFineTuningJobApiResponse = client.createFineTuningJob(request);
System.out.println("model output:" + JSON.toJSONString(createFineTuningJobApiResponse));
}

/**
* 微调文件上传列表查询
*/
private static void testQueryUploadFileList() {
QueryFilesRequest queryFilesRequest = new QueryFilesRequest();
QueryFileApiResponse queryFileApiResponse = client.queryFilesApi(queryFilesRequest);
System.out.println("model output:" + JSON.toJSONString(queryFileApiResponse));
}


/**
* 微调上传数据集
*/
private static void testUploadFile() {
String filePath = "/Users/wujianguo/Downloads/transaction-data.jsonl";
String purpose = "fine-tune";
FileApiResponse fileApiResponse = client.invokeUploadFileApi(purpose, filePath);
System.out.println("model output:" + JSON.toJSONString(fileApiResponse));
}

private static void testEmbeddings() {
EmbeddingRequest embeddingRequest = new EmbeddingRequest();
embeddingRequest.setInput("hello world");
embeddingRequest.setModel(Constants.ModelEmbedding2);
EmbeddingApiResponse apiResponse = client.invokeEmbeddingsApi(embeddingRequest);
System.out.println("model output:" + JSON.toJSONString(apiResponse));
}

/**
* 图生文
*/
private static void testImageToWord() {
List<ChatMessage> messages = new ArrayList<>();
List<Map<String, Object>> contentList = new ArrayList<>();
Map<String, Object> textMap = new HashMap<>();
textMap.put("type", "text");
textMap.put("text", "图里有什么");
Map<String, Object> typeMap = new HashMap<>();
typeMap.put("type", "image_url");
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("url", "https://cdn.bigmodel.cn/enterpriseAc/3f328152-e15c-420c-803d-6684a9f551df.jpeg?attname=24.jpeg");
typeMap.put("image_url", urlMap);
contentList.add(textMap);
contentList.add(typeMap);
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), contentList);
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());


ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4V)
.stream(Boolean.FALSE)
.invokeMethod(Constants.invokeMethod)
.messages(messages)
.requestId(requestId)
.build();
ModelApiResponse modelApiResponse = client.invokeModelApi(chatCompletionRequest);
System.out.println("model output:" + JSON.toJSONString(modelApiResponse));

}

private static void testCreateImage() {
CreateImageRequest createImageRequest = new CreateImageRequest();
createImageRequest.setModel(Constants.ModelCogView);
// createImageRequest.setPrompt("画一个温顺可爱的小狗");
ImageApiResponse imageApiResponse = client.createImage(createImageRequest);
System.out.println("imageApiResponse:" + JSON.toJSONString(imageApiResponse));
}


/**
* sse调用
*/
private static void testSseInvoke() {
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大");
// ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "你能帮我查询2024年1月1日从北京南站到上海的火车票吗?");
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
// 函数调用参数构建部分
List<ChatTool> chatToolList = new ArrayList<>();
ChatTool chatTool = new ChatTool();
chatTool.setType(ChatToolType.FUNCTION.value());
ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
chatFunctionParameters.setType("object");
Map<String, Object> properties = new HashMap<>();
properties.put("departure", new HashMap<String, Object>() {{
put("type", "string");
put("description", "出发城市或车站");
}});
properties.put("destination", new HashMap<String, Object>() {{
put("type", "string");
put("description", "目的地城市或车站");
}});
properties.put("date", new HashMap<String, Object>() {{
put("type", "string");
put("description", "要查询的车次日期");
}});
List<String> required = new ArrayList<>();
required.add("departure");
required.add("destination");
required.add("date");
chatFunctionParameters.setProperties(properties);
ChatFunction chatFunction = ChatFunction.builder()
.name("query_train_info")
.description("根据用户提供的信息,查询对应的车次")
.parameters(chatFunctionParameters)
.required(required)
.build();
chatTool.setFunction(chatFunction);
chatToolList.add(chatTool);

ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.TRUE)
.messages(messages)
.requestId(requestId)
.tools(chatToolList)
.toolChoice("auto")
.build();
ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
if (sseModelApiResp.isSuccess()) {
AtomicBoolean isFirst = new AtomicBoolean(true);
ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable())
.doOnNext(accumulator -> {
{
if (isFirst.getAndSet(false)) {
System.out.print("Response: ");
}
if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls());
System.out.println("tool_calls: " + jsonString);
}
if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
System.out.print(accumulator.getDelta().getContent());
}
}
})
.doOnComplete(System.out::println)
.lastElement()
.blockingGet();

Choice choice = new Choice(chatMessageAccumulator.getChoice().getFinishReason(), 0L, chatMessageAccumulator.getDelta());
List<Choice> choices = new ArrayList<>();
choices.add(choice);
ModelData data = new ModelData();
data.setChoices(choices);
data.setUsage(chatMessageAccumulator.getUsage());
data.setId(chatMessageAccumulator.getId());
data.setCreated(chatMessageAccumulator.getCreated());
data.setRequestId(chatCompletionRequest.getRequestId());
sseModelApiResp.setFlowable(null);
sseModelApiResp.setData(data);
}
System.out.println("model output:" + JSON.toJSONString(sseModelApiResp));
}

public static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable) {
return flowable.map(chunk -> {
return new ChatMessageAccumulator(chunk.getChoices().get(0).getDelta(), null, chunk.getChoices().get(0), chunk.getUsage(), chunk.getCreated(), chunk.getId());
});
}

/**
* 同步调用
*/
private static void testInvoke() {
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大");
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
// 函数调用参数构建部分
List<ChatTool> chatToolList = new ArrayList<>();
ChatTool chatTool = new ChatTool();
chatTool.setType(ChatToolType.FUNCTION.value());
ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
chatFunctionParameters.setType("object");
Map<String, Object> properties = new HashMap<>();
properties.put("location", new HashMap<String, Object>() {{
put("type", "string");
put("description", "城市,如:北京");
}});
properties.put("unit", new HashMap<String, Object>() {{
put("type", "string");
put("enum", new ArrayList<String>() {{
add("celsius");
add("fahrenheit");
}});
}});
chatFunctionParameters.setProperties(properties);
ChatFunction chatFunction = ChatFunction.builder()
.name("get_weather")
.description("Get the current weather of a location")
.parameters(chatFunctionParameters)
.build();
chatTool.setFunction(chatFunction);
chatToolList.add(chatTool);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.FALSE)
.invokeMethod(Constants.invokeMethod)
.messages(messages)
.requestId(requestId)
.tools(chatToolList)
.toolChoice("auto")
.build();
ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
try {
System.out.println("model output:" + mapper.writeValueAsString(invokeModelApiResp));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}


/**
* 异步调用
*/
private static String testAsyncInvoke() {
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatLM和你哪个更强大");
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
// 函数调用参数构建部分
List<ChatTool> chatToolList = new ArrayList<>();
ChatTool chatTool = new ChatTool();
chatTool.setType(ChatToolType.FUNCTION.value());
ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
chatFunctionParameters.setType("object");
Map<String, Object> properties = new HashMap<>();
properties.put("location", new HashMap<String, Object>() {{
put("type", "string");
put("description", "城市,如:北京");
}});
properties.put("unit", new HashMap<String, Object>() {{
put("type", "string");
put("enum", new ArrayList<String>() {{
add("celsius");
add("fahrenheit");
}});
}});
chatFunctionParameters.setProperties(properties);
ChatFunction chatFunction = ChatFunction.builder()
.name("get_weather")
.description("Get the current weather of a location")
.parameters(chatFunctionParameters)
.build();
chatTool.setFunction(chatFunction);
chatToolList.add(chatTool);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.FALSE)
.invokeMethod(Constants.invokeMethodAsync)
.messages(messages)
.requestId(requestId)
.tools(chatToolList)
.toolChoice("auto")
.build();
ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
System.out.println("model output:" + JSON.toJSONString(invokeModelApiResp));
return invokeModelApiResp.getData().getTaskId();
}


/**
* 查询异步结果
*
* @param taskId
*/
private static void testQueryResult(String taskId) {
QueryModelResultRequest request = new QueryModelResultRequest();
request.setTaskId(taskId);
QueryModelResultResponse queryResultResp = client.queryModelResult(request);
try {
System.out.println("model output:" + mapper.writeValueAsString(queryResultResp));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}



(1) 填充自己的信息





(2) 启动示例






这里解释一下四个的区别

  1. testSseInvoke 使用的是逐渐输出,AI回答的结果是一段一段的展示。
  2. testInvoke 使用的是同步执行,当AI全部的回答都输出后才会展示出来。
  3. testAsyncInvoke 与testQueryResult 搭配使用,先通过testAsyncInvoke 让AI去执行,直接返回一个成功或者失败,之后通过获得的taskId,再用testQueryResult去查询获得结果即可。这个过程实践过Bi项目的小伙伴应该深有体会。



(3) 结果展示





这里补充下,如果你要修改问题,只需要修改content参数中的值即可。相当于问AI问题。




参数阅读


其实最关键的就是前三个

A. model

你要选择哪个模型, 例如选择GLM-4 还是GLM-3-Turbo


B. messages

这里需要考虑两个值,一个是role,一般为user即可。role的值官方已经给我们枚举了,只需要调用即可。

剩下的则是我们需要自己填入的content



C. request_id

这个是区分我们每次上传的任务,保证唯一性,可以自己上传一个类似于雪花算法的ID,用户端不传的话平台也会自动生成。


其他参数目前影子测试完前五个方法后发现使用官方默认的即可。只需要你调整好代码的位置以及content的值即可。


剩下的参数,如果你需要使用微调或者向量知识库等高阶玩法,则根据官方文档调整即可。很多地方已经自带了枚举值,只需要直接选择填充。


最后,大家可以用这一个月的免费额度,打造一个属于自己的AI小工具使用,更多玩法,由大家一起探索。


我是影子,一名以Java为主,其余时间探索AI+编程的程序猿。










0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
影子
作者分享
终于拿到鱼厂周边啦! 记录一下😃
18
面试题 | 02 | 非面经,纯分享,辩证看待答案。 #面试题# 压力山大的问题: 可以讲下API签名认证的流程吗? (感觉这种流程题,如果是线下面试,是不是拿出纸,画出流程图给面试官看更好些) 找工作的我: 好的,那其实签名认证的话,它主要是为了保证接口的安全性,防止一些需要成本的,例如使用到AI的API的这种的,万一被攻击,会导致资源被大量消耗。 那API签名认证的话,它涉及到五个参数,首先两个是accessKey和secretKey,它们类似于公钥和密钥,那这个的信息的话,只能让对应的用户看到。 第三个参数的话是sign,标签,我在项目中是采用实现一个工具类去制作的,是用的secretKey 、请求参数和签名算法(MD5)然后生成一个sign1,然后客户端会把这个accessKey 和请求参数一起发送到服务端,那服务端会根据accessKey去数据库查询这个secretKey,接着还是利用刚才提到的工具类去生成sign2,最后比较sign1和sign2是否相同,相同,则说明请求参数没有被篡改。 那其实一般到这里就算基本的流程了,但是为了防止被攻击重放,还需要第四个参数,nonce,我们会在每次请求中生成一个随机的nonce,将这个nonce存入redis中,并设置好TTL,那么往后的请求过来的时候,跟redis中的nonce进行比较,如果一样的话则拒绝请求,如果不存在,才放行请求,那这样的话就可以解决重放的问题。 但是如果说这个攻击者它等到redis中的nonce过期后再进行重放,那它又可以请求成功。对于这种情况,就得使用到第五个参数,timestamp时间戳,假设我们定义的时间戳是一分钟,那服务器只能接收一分钟内的时差请求,那我们在redis设置的nonce的TTL略大于一分钟即可。这样请求重放过来,如果在一分钟内的,因为nonce存在,无法放行。如果请求大于一分钟的,因为超过了一分钟时差的规定,也无法放行。至此,API签名认证所有流程就算完毕了。
13
面试题打卡 | 01 #面试题# 分享 大家好,我是影子。 最近在准备面试题的东西,之前也不断的了解过八股的记忆方式。 个人感觉比较好的方法如下: 1. 通过先了解原理或者答案,同时实践一遍,正所谓好记性不如烂笔头,实践一遍的时候,对于很多细节也能把控住。 2. 对于一个问题,不要仅限于表面,当问题问到A的时候,试想自己是面试官的话,接下来会怎么问这个问题相关的,而不是,对于一个问题,背完就结束,那么稍微问深一点九接不住了。 3. 也是我个人比较喜欢的。 对于多个相关的问题,学习后,整理为一整篇答案,因为很多问题,其实还是可以联系起来的,后面就是通过上下联系的方式,将全部梳理出来,类似于跟别人讲解,当自己能讲出来的时候,这个题,百分之80已经掌握了,剩下的就是偶尔多看看,加深记忆。 本次,以Redis的问题作为分享,因为基本都是自己的口语化方式,模拟跟面试官的对话,当然,如果是大厂的深度拷问,肯定不够用,纯属打卡分享,哪里有误还望大家指出。 压力山大的问题: redisson实现的分布式锁能解决主从一致性的问题吗? 如果业务非要保证数据的强一致性,这个该怎么解决呢? 能介绍一下redis的分布式锁的相关知识吗? .....| 自行补充 找工作的我: 好的,因为在分布式的场景下,一个服务被部署到多个服务器上,那么在并发的情况下,可能导致一个任务被多次执行。那像我们在单机使用的锁,例如syscornized lock锁,就没法解决这个问题。那么,就需要使用到分布式锁。 分布式锁的实现有很多方式,例如redis的setnx命令,这个命令是存入一个key value,如果存入成功,则返回一个1。如果已经有这个key了,那么返回0,存入失败,并且结合lua脚本去实现。但是通过这个方式实现会有很多问题。第一个是流程会比较复杂,成本高。第二个是它的不可重入问题,第三个是它的不可重试问题,第四个是它的超时无法自动续期问题,第五个是主从一致问题。 那基于这些问题,我在项目中采用的是redisson。redisson很好的基于redis去解决了很多分布式的问题,例如分布式限流 分布式锁等等。 首先是不可重入的问题,因为它的底层是一个可重入锁,底层的流程是如果有请求过来,先判断是否已经存入线程号,如果没有,则直接获取锁,如果有,那么判断是否是同一个线程号,如果不是,则直接拒绝。如果是,则给redis中的value值加一,然后重置TTL。那么释放锁的话同样的,给value值减一,直到value为0释放锁,让其他的线程去获取锁。 那第二个的话是不可重试问题,我们使用Redisson的时候,可以定义一个时间,当一个线程获取不到锁的时候,让它不断的自旋进行尝试,直到时间到了,才停止尝试获取。 那第三个的话是不可自动续期问题,redisson中有一个看门狗机制,我们可以在代码中将trylock这个方法中将参数设置为-1,则算开启了这个看门狗机制,那这个机制的话,可以保证我们的业务完整执行,不会说因为当TTL结束后,锁就自动释放了,那具体的话我在实践中看到的是,给的时间是默认30秒,当到达三分之二的时间的时候,就会自动续期。直到我们的业务完整的执行完再释放锁。 那最后的话是主从一致的问题。因为我们底层使用的setnx的命令,那当我们获取到锁的时候,它是写入主节点的,但是因为主节点是异步复制到从节点,那如果说此时主节点宕机了,而线程一获取到锁后又没有释放锁,从节点也没有同步信息。 同时,从节点中会选举一个作为主节点。此时线程二来获取锁,则会获取成功,最终导致出现了一把锁两个线程获取,这是不合理的。 那解决的方式的话,我们可以使用mulitlock这个多重锁,使用这个锁的话,必须保证所有节点都成功的写入获取锁的线程信息后,才算获取成功,那这样就避免了因为redis多节点信息不一致导致的多个线程获取同一把锁的情况。
14
手写 RPC 框架 笔记
11
手写 RPC 框架 笔记
18
下载 APP