草庐IT

系统异常了,发个邮件能解决吗?

IT学习道场 2023-03-28 原文

描述

在我们的项目中,总是有一些我们不可控制的异常,比如数据库连接不上,redis挂掉,以及一些代码上未可知的异常爆发,不能项目上线时就可以统计出来,并且修复,所以当我们这些bug抛出异常时,或者某些可控的严重异常需要推送邮件或者短信或者其他的通讯工具比如 钉钉或者飞书等,我们就需要这样的功能,这里提供一个邮件通知方法,当有未知异常或者被定义为严重异常的,就会给运维人员发送一个邮件进行通知,方便计时应对和问题定位。

解决方案

在springboot中的全局异常捕获处,对不可控异常拿到异常栈信息,进行异常msg的组装和通过freemarker模板进行渲染html文本,然后再把这个异常msg的html进行qq模式的email的发送,freemarker模板可以支撑字符串模板渲染,即渲染的模板字符串可以保存到数据库,也可以直接定义好xxx.ftl模板,都行,这里需要强调的是 渲染的模板字符串可以保存到数据库就更加灵活,可以设计一套freemarker模板的管理系统,比如,对自定义的freemarker模板配置后,保存到数据库,然后根据不同的用户或者企业或者业务,就可以从库中获取对应的freemarker模板,进行数据渲染html,再通过短信或者邮件或者钉钉,这样就实现了类似阿里或者腾讯等第三方的模板配置后进行消息推送的功能。

代码

代码组成包含有自定义的模板的工具jar包和邮件的springboot-starter,以及在微服务中对异常的处理,和调用邮件的消息封装以及模板的创建。

模板自定springboot-starter

详细代码:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>wlc-spring-boot-tools</artifactId>
<groupId>com.wlc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wlc-template</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
    <--freemarker依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
    <--spring-webmvc依赖,这里可以换成spring的ioc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
BeanUtils,处理bean转map。

import org.springframework.cglib.beans.BeanMap;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 描述 实体工具类 <br>
* 作者:IT学习道场 <br>
* 时间:2018/10/26 13:37
*/
public class BeanUtils extends org.springframework.beans.BeanUtils {
public BeanUtils() {
}
/**
* 实例化对象:传入类对类进行实例化对象
*
* @param clazz 类
* @return 对象
* @author Lius
* @date 2018/10/26 13:49
*/
public static <T> T newInstance(Class<?> clazz) {
return (T) instantiateClass(clazz);
}
/**
* 实例化对象,传入类名对该类进行实例化对象
*
* @param clazzStr 类名,传入是必须传入全路径,com...
* @return 对象
* @author Lius
* @date 2018/10/26 13:54
*/
public static <T> T newInstance(String clazzStr) {
try {
Class<?> clazz = Class.forName(clazzStr);
return newInstance(clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException();
}
}
/**
* 把对象封装成Map形式
*
* @param src 需要封装的实体对象
* @author Lius
* @date 2018/10/26 14:08
*/
public static Map toMap(Object src) {
return BeanMap.create(src);
}
/**
* 把Map转换成bean对象
*
* @author Lius
* @date 2018/10/26 14:09
*/
public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
// 对象实例化
T bean = BeanUtils.newInstance(valueType);
PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(valueType);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
String properName = propertyDescriptor.getName();
// 过滤class属性
if ("class".equals(properName)) {
continue;
}
if (beanMap.containsKey(properName)) {
Method writeMethod = propertyDescriptor.getWriteMethod();
if (null == writeMethod) {
continue;
}
Object value = beanMap.get(properName);
if (!writeMethod.isAccessible()) {
writeMethod.setAccessible(true);
}
try {
writeMethod.invoke(bean, value);
} catch (Throwable throwable) {
throw new RuntimeException("Could not set property '" + properName + " ' to bean" + throwable);
}
}
}
return bean;
}
}
FreemarkerUtil,来处理模板的封装。

import com.wlc.template.util.BeanUtils;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.IOException;
import java.util.Map;
/**
* 描述: FreemarkerUtil辅助类 <br>
* 时间: 2022-07-01 9:44 <br>
* 作者:IT学习道场
*/
@Component
public class FreemarkerUtil {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
/**
* bean转map
* @param bean 转换bean
* @return Map<String, Object> - map对象
*/
public Map<String, Object> beanToMap(Object bean){
Map<String, Object> map = BeanUtils.toMap(bean);
return map;
}
/**
* 根据模板路径获取模板渲染数据
* @param templatePath 模板路径 ,ex:templatePath = "notice.ftl",意思是 resources/templates/下的notice.ftl文件
* @param data 渲染数据对象
* @return String- 渲染后的html文本
*/
public String freeMarkerRenderHtml(String templatePath, Map<String, Object> data ){
//获取模板信息
Template template = null;
String html= "";
try {
template = freeMarkerConfigurer.getConfiguration().getTemplate(templatePath);
html = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return html;
}
/**
* 字符文本渲染成html文本
* @param templateName 模板名
* @param templateText 模板文本
* @param data 渲染数据map
* @return String - 渲染后的html文本
*/
public String textRenderFreemarkerHtml(String templateName, String templateText, Map<String, Object> data){
String html = textRenderFreemarkerHtml(templateName, templateText, "utf-8", data);
return html;
}
/**
* 字符文本渲染成html文本
* @param templateName 模板名
* @param templateText 模板文本
* @param charEncode 模板编码
* @param data 渲染数据map
* @return String - 渲染后的html文本
*/
public String textRenderFreemarkerHtml(String templateName, String templateText, String charEncode, Map<String, Object> data){
Template template = textToFreemarkerTemplate(templateName, templateText, charEncode);
String html = freemarkerTemplateRenderHtml(template, data);
return html;
}
/**
* 根据模板对象和数据map渲染html文本
* @param template 模板对象
* @param data 渲染数据map
* @return String - html文本
*/
public String freemarkerTemplateRenderHtml(Template template, Map<String, Object> data){
String html = "";
try {
//渲染data数据到模板中
html = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return html;
}
/**
* 文本转freemarker模板
* @param templateName 模板名字
* @param templateText 模板文本
* @param charEncode 模板编码
* @return Template- 返回freemarkerTemplate对象
*/
public Template textToFreemarkerTemplate(String templateName, String templateText, String charEncode){
//获取配置文件
Configuration cfg = freeMarkerConfigurer.getConfiguration();
//创建freeMarker字符串模板加载器
StringTemplateLoader stringLoader = new StringTemplateLoader();
//加载模板名字和模板文本
stringLoader.putTemplate(templateName, templateText);
//配置文件设置模板加载器
cfg.setTemplateLoader(stringLoader);
freemarker.template.Template template = null;
try {
//从配置文件中获取模板对象
template = cfg.getTemplate(templateName, charEncode);
} catch (IOException e) {
e.printStackTrace();
}
return template;
}
}
这样,模板的渲染jar就封装好了。

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>wlc-spring-boot-tools</artifactId>
<groupId>com.wlc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wlc-email-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 邮件发送的核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- 模板jar -->
<dependency>
<groupId>com.wlc</groupId>
<artifactId>wlc-template</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
EmailUtil-> 邮件发送工具类。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Date;
/**
* 描述: email辅助类 <br>
* 时间: 2022-07-01 10:20 <br>
* 作者:IT学习道场
*/
public class EmailUtil {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private MailProperties mailProperties;
/**
* 发送普通邮件
* @param subject 主题
* @param simpleText 内容
* @param toEamils 邮件接受邮箱数组
*/
public void simpleEmailSend(String subject, String simpleText, String... toEamils) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSentDate(new Date());
message.setFrom(mailProperties.getUsername());
message.setTo(toEamils);
message.setSubject(subject);
message.setText(simpleText);
//发送邮件
javaMailSender.send(message);
}
/**
* 发送html邮件
* @param subject 发送主题
* @param html 发送的html
* @param toEamils 邮件接收人数组
*/
public void emailSendHtml(String subject, String html, String... toEamils) {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper message = null;
try {
message = new MimeMessageHelper(mimeMessage, true);
message.setSentDate(new Date());
message.setFrom(mailProperties.getUsername());
message.setTo(toEamils);
message.setSubject(subject);
message.setText(html, true);
//发送邮件
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
EmailService -> email服务类的辅助类。

import com.wlc.email.util.EmailUtil;
import com.wlc.template.freemarker.FreemarkerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
/**
* 描述: email服务类 <br>
* 时间: 2022-07-01 10:33 <br>
* 作者:IT学习道场
*/
public class EmailService {
@Autowired
private EmailUtil emailUtil;
@Autowired
private FreemarkerUtil freemarkerUtil;
/**
* 根据模板路径给邮件发送html模板
* @param subject 邮件主题
* @param templatePath html模板路径
* @param data 渲染html模板里的数据map
* @param toEamils 邮件接受者数组
*/
public void emailSendHtmlByTemplatePath(String subject, String templatePath, Map<String, Object> data, String... toEamils) {
String html = freemarkerUtil.freeMarkerRenderHtml(templatePath, data);
emailUtil.emailSendHtml(subject, html, toEamils);
}
/**
* 根据字符模板文本给邮件发送html模板
* @param subject 邮件主题
* @param templateName html模板名字
* @param templateText html模板text文本(就是html的模板字符串,使用html模板易字符串的形式保存到数据库,
* 然后从数据库中读取模板字符串,在转换成模板对象,把data进行渲染成html,来发送邮件)
* @param data 渲染html模板里的数据map
* @param toEamils 邮件接受者数组
*/
public void emailSendHtmlByTemplateText(String subject, String templateName, String templateText, Map<String, Object> data, String... toEamils) {
String html = freemarkerUtil.textRenderFreemarkerHtml(templateName, templateText, data);
emailUtil.emailSendHtml(subject, html, toEamils);
}
/**
* 根据字符模板文本给邮件发送html模板
* @param subject 邮件主题
* @param templateName html模板名字
* @param templateText html模板text文本(就是html的模板字符串,使用html模板易字符串的形式保存到数据库,
* 然后从数据库中读取模板字符串,在转换成模板对象,把data进行渲染成html,来发送邮件)
* @param data 渲染html模板里的数据map
* @param charEncode 编码格式 ex: "utf-8"
* @param toEamils 邮件接受者数组
*/
public void emailSendHtmlByTemplateText(String subject, String templateName, String templateText, String charEncode, Map<String, Object> data, String... toEamils) {
String html = freemarkerUtil.textRenderFreemarkerHtml(templateName, templateText, charEncode, data);
emailUtil.emailSendHtml(subject, html, toEamils);
}
}
EmailAutoConfiguration --> 自定义邮件自动化配置类。

import com.wlc.email.service.EmailService;
import com.wlc.email.util.EmailUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述: 自定义邮件自动化配置类 <br>
* 时间: 2022-07-01 11:00 <br>
* 作者:IT学习道场
*/
@Configuration
public class EmailAutoConfiguration {
@Bean
public EmailService emailService(){
EmailService emailService = new EmailService();
return emailService;
}
@Bean
EmailUtil emailUtil(){
EmailUtil emailUtil = new EmailUtil();
return emailUtil;
}
}
resources/META-INF下的spring.factories文件。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wlc.email.config.EmailAutoConfiguration
这样一个邮件的自定义springboot-starter就封装好了。

下面就是在各个微服务中的全局异常捕获中进行msg的freemarker的组装和邮件的发送。

演示的服务代码,全局异常处理。

ftl代码,有类似需求可以copy。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"></meta>
<title>邮件内容</title>
</head>
<body>
<p style="text-align:center "><img width="400px" height="200px" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimgsa.baidu.com%2Fexp%2Fw%3D500%2Fsign%3Da5c70c27a1efce1bea2bc8ca9f50f3e8%2Fa9d3fd1f4134970a05665ffe93cad1c8a6865dcd.jpg&refer=http%3A%2F%2Fimgsa.baidu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659171243&t=17d3f0b344e9cb402e8ec4311207fa5a"></p>
<h4>系统管理员</h4>
<h4> 您好!</h4>
<p>系统异常了!日志如下:</p>
<p>服务名:<span style="color:red">${appName}</span></p>
<p>服务ip:<span style="color:red">${ipAddr}</span></p>
<p>类路径:<span style="color:red">${className}</span></p>
<p>方法名字:<span style="color:red">${methodName}</span></p>
<p>异常发生行号:<span style="color:red">${lineNumber}</span></p>
<p style="color:red">${content}</p>
<p style="width: 100%;text-align: right">IT学习道场系统</p>
</body>
</html>

EmailExtendContent -> email的异常邮件信息组装类,这里是给邮件哪个模板里需要的数据组装。

import com.utils.IPUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.servlet.http.HttpServletRequest;
/**
* 描述: email的异常邮件信息组装类 <br>
* 时间: 2022-06-30 17:03 <br>
* 作者:IT学习道场
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class EmailExtendContent {
//异常
public Exception e;
//请求request
public HttpServletRequest request;
//ip地址
public String ipAddr;
//类命
public String className;
//方法名
public String methodName;
//发生异常的行号
public int lineNumber;
public EmailExtendContent(Exception e, HttpServletRequest request) {
this.e = e;
this.request = request;
handler();
}
void handler(){
StackTraceElement stackTraceElement = e.getStackTrace()[0];
// 获取类名
ipAddr = IPUtil.getIpAddr(request);
className = stackTraceElement.getClassName();
methodName = stackTraceElement.getMethodName();
lineNumber = stackTraceElement.getLineNumber();
}
}
EmailHandler --> 微服务中email处理类定制化处理类。

import com.wlc.email.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 描述: email处理类 <br>
* 时间: 2022-07-01 11:11 <br>
* 作者:IT学习道场
*/
@Component
public class EmailHandler {
@Autowired
private EmailService emailService; // 自定义邮件starter里的 EmailService
@Value("${spring.application.name:no-service}") //服务应用没名字
private String appName;
@Value("#{'${spring.mail.toEamils:}'.split(',')}") //邮件接收者的邮箱数组
private String[] toEamils;
/**
* 异步发送邮件
* @param stackExceptionMsg 栈异常信息 str
* @param e 异常对象
* @param request request
*/
@Async
public void sendExceptionEmail(String stackExceptionMsg,Exception e, HttpServletRequest request){
//根据异常对象和request组装EmailExtendContent
EmailExtendContent emailExtendContent = new EmailExtendContent(e, request);
//组装渲染数据data
Map<String, Object> data = builderData(appName, emailExtendContent, stackExceptionMsg);
//发送邮件
emailService.emailSendHtmlByTemplatePath("美术传媒系统异常报告", "notice.ftl", data, toEamils);
}
private Map builderData(String appName, EmailExtendContent extendContent, String content){
Map<String, Object> data = new HashMap<>();
data.put("appName", appName);
data.put("ipAddr", extendContent.ipAddr);
data.put("className", extendContent.className);
data.put("methodName", extendContent.methodName);
data.put("lineNumber", extendContent.lineNumber);
data.put("content", content);
return data;
}
}
然后就可以在全局异常里调用一下 EmailHandler.sendExceptionEmail即可。

import cn.hutool.core.exceptions.ExceptionUtil;
import com.email.EmailHandler;
import com.http.constant.HttpCode;
import com.http.exception.BusinessException;
import com.utils.JsonUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* 全局结果响应处理,全局异常处理 <br>
* 作者:IT学习道场 <br>
* 时间:2019-01-24 10:33
*/
@Slf4j
@RestControllerAdvice
public class ResultAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private EmailHandler emailHandler;
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
/**
* 先捕获异常 然后再把数据返回到ResponseBody中,
* 然后在Body中要返回数据的时候调用上面的拦截方法beforeBodyWrite()
*/
@ExceptionHandler(value = Exception.class)
public Object handleException(Object o, Exception e, HttpServletRequest request) {
//此处返回json数据
//捕捉到的异常如果是自定义异常类,那么就返回自定义异常类中的错误码和错误信息
String stackExceptionMsg = ExceptionUtil.stacktraceToString(e);
//异常输出到日志
log.error(stackExceptionMsg);
//自定义基础异常
if (e instanceof BusinessException) {
return new ResultException(((BusinessException) e).getCode(), false, ((BusinessException) e).getMessage(), request.getRequestURL().toString());
//非法参数异常
} else if (e instanceof IllegalArgumentException) {
return new ResultException(HttpCode.BAD_REQUEST.code, false, "参数异常,请稍候再试", request.getRequestURL().toString());
//绑定异常
} else if (e instanceof BindException) {
return new ResultException(HttpCode.BAD_REQUEST.code, false, ((BindException) e).getBindingResult().getFieldError().getDefaultMessage(), request.getRequestURL().toString());
//方法参数异常验证异常
} else if (e instanceof MethodArgumentNotValidException) {
return new ResultException(HttpCode.BAD_REQUEST.code, false, ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage(), request.getRequestURL().toString());
}
//这里是除了自定义异常的其他异常信息
else {
       // 这里是未知不可控异常,发送异常邮件即可
emailHandler.sendExceptionEmail(stackExceptionMsg, e, request);
return new ResultException(HttpCode.SERVER_ERROR.code, false, "系统异常请联系管理员", request.getRequestURL().toString());
}
}
}
下面是application.yml的邮件配置。

erver:
port: 8080
#启用undertow
undertow:
# CPU有几核,就填写几。
io-threads: 4
#阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
worker-threads: 32
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
buffer-size: 1024
# 是否分配的直接内存(NIO直接分配的堆外内存)
direct-buffers: true
servlet:
context-path: /
spring:
application:
name: wlc
redis:
host: localhost
port: 6379
password:
lettuce:
pool:
# 连接池中的最大空闲连接 默认8
max-idle: 8
# 连接池中的最小空闲连接 默认0
min-idle: 0
# 连接池最大连接数 默认8 ,负数表示没有限制
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
max-wait: -1
#, 1603610130@qq.com
mail:
host: smtp.qq.com
  #你开通smtp的邮箱地址
username: xxxxx@qq.com
  #你的邮箱开通smtp时的授权码
password: xxxxxxxxxx
port: 465
toEamils: xxxx@qq.com,yyyy@qq.com
properties:
mail:
smtp:
auth: true
ssl:
enable: true
starttls:
enable: true
required: true
验证,已通知找个接口,搞个异常测试,我的是 int i= 1/0,简单测试。


有关系统异常了,发个邮件能解决吗?的更多相关文章

  1. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

  2. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  3. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  4. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  5. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  6. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  9. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  10. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

随机推荐