RabbitMq 消费失败,重试机制

方案一:

本地消息表 + 定时任务
本地消息表:主要用于存储 业务数据、交换机、队列、路由、次数
定时任务:定时扫描本地消息表,重新给业务队列投递消息。
具体思路:业务队列消费失败时,把 业务数据、交换机、队列、路由、次数(需要重新计算) 存储在本地消息表里,然后定时任务去扫描本地消息表,把符合条件(是否满足重试次数,是否达到重试时间)的数据筛选出来进行二次投递,消费者正常消费,在消费失败时需要入库。

方案二:

利用 rabbitmq_delayed_message_exchange 插件 实现延迟队列
具体思路:业务队列消费失败时,给延迟队列发送一条消息,消息包含业务数据、交换机、队列、次数、最大次数等,延迟队列收到消息后重新给业务队列投递消息。业务队列二次收到消息时,再次消费失败,校验最大次数,判断是否再次重试。

具体实现

  1. pom.xml
    <dependencies><dependency><groupId>run.siyuan</groupId><artifactId>siyuan-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
  1. application.yml
server:
port: 8080spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: siyuan
password: siyuan123456virtual-host: /
  1. PluginDelayRabbitConfig.java

import com.rabbitmq.client.ConnectionFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;/*** @className: PluginDelayRabbitConfig* @Description: TODO* @author: wzq* @date: 2022/5/17 10:50 AM*/
@Configuration
public class PluginDelayRabbitConfig {@Bean("pluginDelayExchange")public CustomExchange pluginDelayExchange() {Map<String, Object> argMap = new HashMap<>();argMap.put("x-delayed-type", "direct");//必须要配置这个类型,可以是direct,topic和fanout//第二个参数必须为x-delayed-messagereturn new CustomExchange("PLUGIN_DELAY_EXCHANGE","x-delayed-message",false, false, argMap);}@Bean("pluginDelayQueue")public Queue pluginDelayQueue(){return new Queue("PLUGIN_DELAY_QUEUE");}@Beanpublic Binding pluginDelayBinding(@Qualifier("pluginDelayQueue") Queue queue, @Qualifier("pluginDelayExchange") CustomExchange customExchange){return BindingBuilder.bind(queue).to(customExchange).with("delay").noargs();}
}
  1. RabbitmqConsumer.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;/*** @className: consumer* @Description: TODO* @author: wzq* @date: 2022/5/17 10:52 AM*/
@Slf4j
@Component
public class RabbitmqConsumer {@RabbitHandler@RabbitListener(queues = "PLUGIN_DELAY_QUEUE")//监听延时队列public void fanoutConsumer(String msg){SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("【插件延迟队列】【" + sdf.format(new Date()) + "】收到消息:" + msg);}
}
  1. RabbitMqController.java
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.text.SimpleDateFormat;
import java.util.Date;/*** @className: RabbitMqController* @Description: TODO* @author: wzq* @date: 2022/5/17 10:54 AM*/
@RestController
public class RabbitMqController {@AutowiredRabbitTemplate rabbitTemplate;@GetMapping(value = "/plugin/send")public String pluginMsgSend(@RequestParam Integer time) {JSONObject json = new JSONObject();json.set("name", "插件延迟消息");json.set("time", System.currentTimeMillis());json.set("delayTime", time);MessageProperties messageProperties = new MessageProperties();messageProperties.setHeader("x-delay", 1000 * time);//延迟5秒被删除Message message = new Message(JSONUtil.toJsonStr(json).getBytes(), messageProperties);rabbitTemplate.convertAndSend("PLUGIN_DELAY_EXCHANGE", "delay", message);//交换机和路由键必须和配置文件类中保持一致SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("消息发送成功【" + sdf.format(new Date()) + "】" + "延迟时间:" + time);return "succ";}
}

方案三:

利用 TTL 消息 + DLX 死信队列 实现延迟队列
具体思路:业务队列消费失败时,会发送一条TTL 消息,消息包含业务数据、交换机、队列、次数、最大次数等,TTL 消息过期后会进入死信队列,此时监听死信队列接收消息,校验是否达到重试次数,再重新投递给业务队列,业务队列二次收到消息时,再次消费失败,校验最大次数,判断是否再次重试。超过最大次数入库,人工干预处理

具体实现

  1. pom.xml
    <dependencies><dependency><groupId>run.siyuan</groupId><artifactId>siyuan-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
  1. application.yml
server:
port: 8080spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: siyuan
password: siyuan123456virtual-host: /
  1. Constants.java
public interface Constants {// ------------------------------ delay -------------------------------------// 延时交换机String DELAY_EXCHANGE = "delay.exchange";// 延时交换机队列String DELAY_EXCHANGE_QUEUE = "delay.exchange.queue";// 延时交换机路由键String DELAY_EXCHANGE_ROUTE_KEY = "delay.exchange.route.key";// ------------------------------ dead.letter.fanout -------------------------------------// 死信交换机String DELAY_LETTER_EXCHANGE = "dead.letter.exchange";// 死信交换机队列String DELAY_LETTER_EXCHANGE_QUEUE = "dead.letter.exchange.queue";// 死信交换机路由键String DELAY_LETTER_EXCHANGE_ROUTE_KEY = "dead.letter.exchange.route.key";// ------------------------------ 业务队列 -------------------------------------String SERVICE_QUEUE = "service.queue";}
  1. RetryRabbitConfig.java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import run.siyuan.common.rabbitmq.Constants;import java.util.HashMap;
import java.util.Map;@Configuration
public class RetryRabbitConfig {/*** ------------------- 延时队列相关 -------------------* @return*//*** 延时交换机*/@Beanpublic DirectExchange ttlDelayExchangeRetry() {return new DirectExchange(Constants.DELAY_EXCHANGE);}/*** 延时交换机队列*/@Beanpublic Queue ttlDelayExchangeQueueRetry() {Map<String, Object> map = new HashMap<String, Object>();//队列中所有消息5秒后过期//map.put("x-message-ttl", 1000 * 60 * 5);//过期后进入死信队列map.put("x-dead-letter-exchange", Constants.DELAY_LETTER_EXCHANGE);return new Queue(Constants.DELAY_EXCHANGE_QUEUE, false, false, false, map);}/*** Fanout交换机和productQueue绑定*/@Beanpublic Binding bindTtlExchangeAndQueueRetry() {return BindingBuilder.bind(ttlDelayExchangeQueueRetry()).to(ttlDelayExchangeRetry()).with(Constants.DELAY_EXCHANGE_ROUTE_KEY);}/*** ------------------- 死信队列相关 -------------------*//*** fanout死信交换机** @return*/@Beanpublic FanoutExchange deadLetterExchange() {return new FanoutExchange(Constants.DELAY_LETTER_EXCHANGE);}/*** 死信队列** @return*/@Beanpublic Queue deadLetterQueue() {return new Queue(Constants.DELAY_LETTER_EXCHANGE_QUEUE);}/*** 正常业务队列* @return*/@Beanpublic Queue serviceQueue() {return new Queue(Constants.SERVICE_QUEUE);}/*** 死信队列和死信交换机绑定** @return*/@Beanpublic Binding deadLetterBind() {return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());}
}
  1. MessageRetryVo
package run.siyuan.rabbitmq.retry.message.model;import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;import java.io.Serializable;
import java.util.Date;@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class MessageRetryVo implements Serializable {private static final long serialVersionUID = 1L;/*** 原始消息body*/private String bodyMsg;/*** 交换器*/private String exchangeName;/*** 路由键*/private String routingKey;/*** 队列*/private String queueName;/*** 最大重试次数*/private Integer maxTryCount = 3;/*** 当前重试次数*/private Integer currentRetryCount = 0;/*** 任务失败信息*/private String errorMsg;/*** 创建时间*/private Date createTime;/*** 消息类型 0.延时消息 1.重试消息*/private Integer type;@Overridepublic String toString() {return "MessageRetryDTO{" +"bodyMsg='" + bodyMsg + '\'' +", exchangeName='" + exchangeName + '\'' +", routingKey='" + routingKey + '\'' +", queueName='" + queueName + '\'' +", maxTryCount=" + maxTryCount +", currentRetryCount=" + currentRetryCount +", errorMsg='" + errorMsg + '\'' +", createTime=" + createTime +'}';}/*** 检查重试次数是否超过最大值** @return*/public boolean checkRetryCount(Integer type) {//检查重试次数是否超过最大值if (this.currentRetryCount <= this.maxTryCount) {if (type.equals(0)) {retryCountCalculate();}return true;}return false;}/*** 重新计算重试次数*/private void retryCountCalculate() {this.currentRetryCount = this.currentRetryCount + 1;}}
  1. ServiceConsumer.java

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import run.siyuan.common.rabbitmq.Constants;
import run.siyuan.rabbitmq.retry.message.service.CommonMessageDelayService;import java.io.IOException;/**
* 正常消费
*/
@Slf4j
@Component
public class ServiceConsumer extends CommonMessageDelayService {@RabbitListener(queues = Constants.SERVICE_QUEUE, ackMode = "MANUAL", concurrency = "1")private void consumer(Message message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {try {byte[] body = message.getBody();String msg = new String(body);log.info("【正常队列】【" + System.currentTimeMillis() + "】收到死信队列消息:" + msg);JSONObject json = JSONUtil.parseObj(msg);if (json.getInt("id") < 0) {throw new Exception("id 小于 0");}channel.basicAck(deliveryTag, false);} catch (Exception e) {log.info("消费异常:{}", e.getMessage());channel.basicNack(deliveryTag, false, false);sendDelayMessage(message, e);}}}
  1. DeadLetterConsumer

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import run.siyuan.common.rabbitmq.Constants;
import run.siyuan.rabbitmq.retry.message.service.CommonMessageRetryService;import java.io.IOException;/*** @className: DeadLetterConsumer* @Description: TODO 死信队列消费者* @author: wzq* @date: 2022/5/13 3:05 PM*/
@Slf4j
@Component
public class DeadLetterConsumer extends CommonMessageRetryService {@RabbitHandler@RabbitListener(queues = Constants.DELAY_LETTER_EXCHANGE_QUEUE, ackMode = "MANUAL", concurrency = "1")public void consumer(Message message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {try {log.info("【死信队列】【" + System.currentTimeMillis() + "】收到死信队列消息:", new String(message.getBody()));retryMessage(message);channel.basicAck(deliveryTag, false);} catch (Exception e) {channel.basicNack(deliveryTag, false, false);}}
}
  1. CommonMessageDelayService.java

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import run.siyuan.common.rabbitmq.Constants;
import run.siyuan.rabbitmq.retry.message.model.MessageRetryVo;/*** @className: TtlTetsConsumer* @Description: TODO rabbitmq 补偿机制--发送延时消息* @author: wzq* @date: 2022/5/13 3:05 PM*/@Slf4j
public abstract class CommonMessageDelayService extends AbstractCommonMessageService {@Autowiredprivate RabbitTemplate rabbitTemplate;/*** 发送延时消息** @param message*/protected void sendDelayMessage(Message message, Exception e) {try {//封装消息MessageRetryVo delayMessageVo = buildMessageRetryInfo(message);log.info("延时消息:{}", delayMessageVo);//获取所有堆栈信息StackTraceElement[] stackTraceElements = e.getStackTrace();//默认的异常类全路径为第一条异常堆栈信息的String exceptionClassTotalName = stackTraceElements[0].toString();//遍历所有堆栈信息,找到vip.xiaonuo开头的第一条异常信息for (StackTraceElement stackTraceElement : stackTraceElements) {if (stackTraceElement.toString().contains("com.central")) {exceptionClassTotalName = stackTraceElement.toString();break;}}log.info("异常信息:{}", exceptionClassTotalName);delayMessageVo.setErrorMsg(exceptionClassTotalName);delayMessageVo.setType(0);prepareAction(delayMessageVo);} catch (Exception exception) {log.warn("处理消息异常,错误信息:", exception);}}/*** 异常消息重新入库** @param retryVo*/@Overrideprotected void sendMessage(MessageRetryVo retryVo) {//将补偿消息实体放入头部,原始消息内容保持不变MessageProperties messageProperties = new MessageProperties();// 消息的有效时间固定,不使用自定义时间messageProperties.setExpiration(String.valueOf(1000 * 10 * 1));messageProperties.setHeader("message_retry_info", JSONUtil.toJsonStr(retryVo));Message ttlMessage = new Message(JSONUtil.toJsonStr(retryVo).getBytes(), messageProperties);rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE, Constants.DELAY_EXCHANGE_ROUTE_KEY, ttlMessage);log.info("发送业务消息 完成 时间:{}", System.currentTimeMillis());}}
  1. CommonMessageRetryService.java
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import run.siyuan.rabbitmq.retry.message.model.MessageRetryVo;/*** @className: TtlTetsConsumer* @Description: TODO rabitmq 补偿机制--重新发送业务消息* @author: wzq* @date: 2022/5/13 3:05 PM*/
@Slf4j
public abstract class CommonMessageRetryService extends AbstractCommonMessageService {@Autowiredprivate RabbitTemplate rabbitTemplate;/*** 发送延时消息** @param message*/public void retryMessage(Message message) {try {//封装消息MessageRetryVo retryMessageVo = buildMessageRetryInfo(message);log.info("重试消息:{}", retryMessageVo);retryMessageVo.setType(1);prepareAction(retryMessageVo);} catch (Exception exception) {log.warn("处理消息异常,错误信息:", exception);}}/*** 异常消息重新入库** @param retryVo*/@Overrideprotected void sendMessage(MessageRetryVo retryVo) {//将补偿消息实体放入头部,原始消息内容保持不变MessageProperties messageProperties = new MessageProperties();messageProperties.setHeader("message_retry_info", JSONUtil.toJsonStr(retryVo));Message message = new Message(retryVo.getBodyMsg().getBytes(), messageProperties);rabbitTemplate.convertAndSend(retryVo.getExchangeName(), retryVo.getRoutingKey(), message);log.info("发送业务消息 完成 时间:{}", System.currentTimeMillis());}}
  1. AbstractCommonMessageService
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import run.siyuan.rabbitmq.retry.message.model.MessageRetryVo;import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.Objects;/*** @className: TtlTetsConsumer* @Description: TODO rabbitmq 补偿机制 抽象类* @author: wzq* @date: 2022/5/13 3:05 PM*/
@Slf4j
public abstract class AbstractCommonMessageService {/*** 发送消息** @param retryVo*/protected abstract void sendMessage(MessageRetryVo retryVo);/*** 构建消息补偿实体** @param message* @return*/protected MessageRetryVo buildMessageRetryInfo(Message message) {//如果头部包含补偿消息实体,直接返回Map<String, Object> messageHeaders = message.getMessageProperties().getHeaders();if (messageHeaders.containsKey("message_retry_info")) {Object retryMsg = messageHeaders.get("message_retry_info");if (Objects.nonNull(retryMsg)) {return JSONUtil.toBean(JSONUtil.parseObj(retryMsg), MessageRetryVo.class);}}//自动将业务消息加入补偿实体MessageRetryVo messageVo = new MessageRetryVo();messageVo.setBodyMsg(new String(message.getBody(), StandardCharsets.UTF_8));messageVo.setExchangeName(message.getMessageProperties().getReceivedExchange());messageVo.setRoutingKey(message.getMessageProperties().getReceivedRoutingKey());messageVo.setQueueName(message.getMessageProperties().getConsumerQueue());messageVo.setCreateTime(new Date());return messageVo;}/*** 准备执行** @param messageVo*/protected void prepareAction(MessageRetryVo messageVo) {if (messageVo.checkRetryCount(messageVo.getType())) {this.sendMessage(messageVo);} else {if (log.isWarnEnabled()) {log.warn("当前任务重试次数已经到达最大次数,业务数据:" + messageVo.toString());}doFailCallBack(messageVo);}}/*** 重试失败,回调服务** @param messageVo*/protected void doFailCallBack(MessageRetryVo messageVo) {try {saveRetryMessageInfo(messageVo);} catch (Exception e) {log.warn("执行失败回调异常,错误原因:{}", e.getMessage());}}/*** 将异常消息入库** @param messageVo*/protected void saveRetryMessageInfo(MessageRetryVo messageVo) {try {log.info("重试消息次数:{} message_retry_info:{}", messageVo.getCurrentRetryCount(), messageVo);} catch (Exception e) {log.error("将异常消息存储到mongodb失败,消息数据:" + messageVo.toString(), e);}}
}
  1. RetryController.java

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import run.siyuan.common.rabbitmq.Constants;@Slf4j
@RestController
@RequestMapping("/retry")
public class RetryController {@Autowiredprivate RabbitTemplate rabbitTemplate;@GetMapping(value = "/service/message")public String consumerFailQueue(@RequestParam(required = false, defaultValue = "1") Integer id) {JSONObject json = new JSONObject();json.set("id", id);json.set("name", "消息名称");json.set("time", System.currentTimeMillis());String msg = StrUtil.format("消息发送时间:{} 消息数据:{}", System.currentTimeMillis(), json);log.info(msg);rabbitTemplate.convertAndSend(Constants.SERVICE_QUEUE, json);log.info("消息发送完成时间:{}", System.currentTimeMillis());return "success";}
}

PS:方案三会照成消息的阻塞,例如:发送第一个延时消息,10分钟过期,再发送第二个延时消息,5分钟过期。第二个消息肯定要比第一个消息提前过期,但此时因为前一个消息没有过期也就没有出队列,那第二个消息只能等待第一个出队列之后才能出队列。这样就照成了消息的阻塞。业务上允许的情况下,可以使用这种方式。

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

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

相关文章

Android常用的工具类

主要介绍总结的Android开发中常用的工具类&#xff0c;大部分同样适用于Java。目前包括HttpUtils、DownloadManagerPro、ShellUtils、PackageUtils、 PreferencesUtils、JSONUtils、FileUtils、ResourceUtils、StringUtils、 ParcelUtils、RandomUtils、ArrayUtils、ImageUtils…

0. Spring 基础

BeanDefinition BeanDefinition 表示 Bean 定义&#xff1a; Spring根据BeanDefinition来创建Bean对象&#xff1b;BeanDefinition有很多的属性用来描述Bean&#xff1b;BeanDefiniton是Spring中非常核心的概念。BeanDefiniton中重要的属性&#xff1a; a. beanClass&#xf…

1. Spring 源码:Spring 解析XML 配置文件,获得 Bena 的定义信息

通过 Debug 运行 XmlBeanDefinitionReaderTests 类的 withFreshInputStream() 的方法&#xff0c;调试 Spring 解析 XML 配置文件&#xff0c;获得 Bean 的定义。 大体流程可根据序号查看&#xff0c;xml 配置文件随便看一眼&#xff0c;不用过多在意。 <?xml version&qu…

c++ 读取文件 最后一行读取了两次

用ifstream的eof()&#xff0c;竟然读到文件最后了&#xff0c;判断eof还为false。网上查找资料后&#xff0c;终于解决这个问题。 参照文件&#xff1a;http://tuhao.blogbus.com/logs/21306687.html 在使用C/C读文件的时候&#xff0c;一定都使用过eof&#xff08;&#xff0…

java中的io系统详解(转)

Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符&#xff0c;分别操作字符、字符数组或字符串&#xff0c;而字节流处理单元为 1 个字节&#xff0c;操作字节和字节数组。 Java 内用 Unicode 编码存储字符&#xff0c;字符流处理类负责将外部的…

js获取字符串最后一个字符代码

方法一&#xff1a;运用String对象下的charAt方法 charAt() 方法可返回指定位置的字符。 代码如下 复制代码 str.charAt(str.length – 1) 请注意&#xff0c;JavaScript 并没有一种有别于字符串类型的字符数据类型&#xff0c;所以返回的字符是长度为 1 的字符串 方法二&#…

Unity3D Shader入门指南(二)

关于本系列 这是Unity3D Shader入门指南系列的第二篇&#xff0c;本系列面向的对象是新接触Shader开发的Unity3D使用者&#xff0c;因为我本身自己也是Shader初学者&#xff0c;因此可能会存在错误或者疏漏&#xff0c;如果您在Shader开发上有所心得&#xff0c;很欢迎并恳请您…

JVM:如何分析线程堆栈

英文原文&#xff1a;JVM: How to analyze Thread Dump 在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因。在我看来线程堆栈分析技术是Java EE产品支持工程师所必须掌握的一门技术。在线程堆栈中存储的信息&#xff0c;通常远超出你的想象&…

一个工科研究生毕业后的职业规划

http://blog.csdn.net/wojiushiwo987/article/details/8592359一个工科研究生毕业后的职业规划 [wojiushiwo987个人感触]:说的很诚恳&#xff0c;对于马上面临毕业的我很受用&#xff0c;很有启发。有了好的职业生涯规划&#xff0c;才有了前进的方向和动力&#xff0c;才能…

SQLSERVER中如何忽略索引提示

SQLSERVER中如何忽略索引提示 原文:SQLSERVER中如何忽略索引提示SQLSERVER中如何忽略索引提示 当我们想让某条查询语句利用某个索引的时候&#xff0c;我们一般会在查询语句里加索引提示&#xff0c;就像这样 SELECT id,name from TB with (index(IX_xttrace_bal)) where bal…

JavaScript——以简单的方式理解闭包

闭包&#xff0c;在一开始接触JavaScript的时候就听说过。首先明确一点&#xff0c;它理解起来确实不复杂&#xff0c;而且它也非常好用。那我们去理解闭包之前&#xff0c;要有什么基础呢&#xff1f;我个人认为最重要的便是作用域&#xff08;lexical scope&#xff09;&…

jquery实现二级联动不与数据库交互

<select id"pro" name"pro" style"width:90px;"></select> <select id"city" name"city" style"width: 90px"></select> $._cityInfo [{"n":"北京市","c"…

[016]转--C++拷贝构造函数详解

一. 什么是拷贝构造函数 首先对于普通类型的对象来说&#xff0c;它们之间的复制是很简单的&#xff0c;例如&#xff1a; [c-sharp] view plaincopy int a 100; int b a; 而类对象与普通对象不同&#xff0c;类对象内部结构一般较为复杂&#xff0c;存在各种成员变量。下…

js中调用C标签实现百度地图

<script type"text/javascript"> //json数组 var jsonArray document.getElementById("restaurant").value; var map new BMap.Map("milkMap"); // 创建地图实例 <c:forEach items"${restaurantlist}" var"…

jquery较验组织机构编码

//*************************组织机构码较验************************* function checkOrganizationCode() { var weight [3, 7, 9, 10, 5, 8, 4, 2]; var str 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ; var reg /^([0-9A-Z]){8}-[0-9|X]{1}$/; var organizationcode $("…

自定义GrildView实现单选功能

首先看实现功能截图&#xff0c;这是一个自定义Dialog,并且里面内容由GrildView 绑定数据源&#xff0c;实现类似单选功能。 首先自定义Dialog&#xff0c;绑定数据源 自定义Dialog弹出框大小方法 最主要实现的就是点击颜色切换的功能&#xff0c;默认GrildView的每一项都是蓝色…

Java数字字符串如何转化为数字数组

eg&#xff1a; String numberString "0123456789"; 如何转化为&#xff1a;int[] digitArry new int[]{0,1,2,3,4,5,6,7,8,9}; 解决办法&#xff1a; char[] digitNumberArray numberString.toCharArray(); int[] digitArry new int[digitString.toCharArray().l…

『重构--改善既有代码的设计』读书笔记----序

作为C的程序员&#xff0c;我从大学就开始不间断的看书&#xff0c;看到如今上班&#xff0c;也始终坚持每天多多少少阅读技术文章&#xff0c;书看的很多&#xff0c;但很难有一本书&#xff0c;能让我去反复的翻阅。但唯独『重构--改善既有代码的设计』这本书让我重复看了不下…

微信公共平台接口开发--Java实现

Java微信实现&#xff0c;采用SpringMVC 架构&#xff0c;采用SAXReader解析XML RequestMapping(value"/extend") public class WeixinController { RequestMapping(value"/weixin") public ModelAndView weixin(HttpServletRequest request,HttpServlet…

最大权闭合图hdu3996

定义&#xff1a;最大权闭合图&#xff1a;是有向图的一个点集&#xff0c;且该点集的所有出边都指向该集合。即闭合图内任意点的集合也在改闭合图内&#xff0c;给每个点分配一个点权值Pu&#xff0c;最大权闭合图就是使闭合图的点权之和最大。 最小割建边方式&#xff1a;源点…