Spring 常犯的十大错误,打死都不要犯!

戳蓝字“CSDN云计算”关注我们哦!  

640?wx_fmt=jpeg

作者 | Toni Kukurin
责编 | 刘丹

错误一:太过关注底层

我们正在解决这个常见错误,是因为 “非我所创” 综合症在软件开发领域很是常见。症状包括经常重写一些常见的代码,很多开发人员都有这种症状。

虽然理解特定库的内部结构及其实现,在很大程度上是好的并且很有必要的(也可以是一个很好的学习过程),但作为软件工程师,不断地处理相同的底层实现细节对个人的开发生涯是有害的。

像 Spring 这种抽象框架的存在是有原因的,它将你从重复地手工劳作中解放出来,并允许你专注于更高层次的细节 —— 领域对象和业务逻辑。

因此,接受抽象。下次面对特定问题时,首先进行快速搜索,确定解决该问题的库是否已被集成到 Spring 中;现在,你可能找到一个合适的现成解决方案。比如,一个很有用的库,在本文的其他部分,我将在示例中使用 Project Lombok 注解。Lombok 被用作样板代码生成器,希望懒惰的开发人员在熟悉这个库时不会遇到问题。

错误二:内部结构 “泄露”

公开你的内部结构,从来都不是一个好主意,因为它在服务设计中造成了不灵活性,从而促进了不好的编码实践。“泄露” 的内部机制表现为使数据库结构可以从某些 API 端点访问。例如,下面的 POJO(“Plain Old Java Object”)类表示数据库中的一个表:

@Entity	
@NoArgsConstructor	
@Getter	
public class TopTalentEntity {	@Id	@GeneratedValue	private Integer id;	@Column	private String name;	public TopTalentEntity(String name) {	this.name = name;	}	}

假设,存在一个端点,他需要访问 TopTalentEntity 数据。返回 TopTalentEntity 实例可能很诱人,但更灵活的解决方案是创建一个新的类来表示 API 端点上的 TopTalentEntity 数据。

@AllArgsConstructor	
@NoArgsConstructor	
@Getter	
public class TopTalentData {	private String name;	
}

这样,对数据库后端进行更改将不需要在服务层进行任何额外的更改。考虑下,在 TopTalentEntity 中添加一个 “password” 字段来存储数据库中用户密码的 Hash 值 —— 如果没有 TopTalentData 之类的连接器,忘记更改服务前端,将会意外地暴露一些不必要的秘密信息。

错误三:缺乏关注点分离

随着程序规模的增长,逐渐地,代码组织成为一个越来越重要的问题。讽刺的是,大多数好的软件工程原则开始在规模上崩溃 —— 特别是在没有太多考虑程序体系结构设计的情况下。开发人员最常犯的一个错误就是混淆代码关注点,这很容易做到!

通常,打破 关注点分离 的是将新功能简单地 “倒” 在现有类中。当然,这是一个很好的短期解决方案(对于初学者来说,它需要更少的输入),但它也不可避免地会在将来成为一个问题,无论是在测试期间、维护期间还是介于两者之间。考虑下下面的控制器,它将从数据库返回 TopTalentData。

@RestController	
public class TopTalentController {	private final TopTalentRepository topTalentRepository;	@RequestMapping("/toptal/get")	public List<TopTalentData> getTopTalent() {	return topTalentRepository.findAll()	.stream()	.map(this::entityToData)	.collect(Collectors.toList());	}	private TopTalentData entityToData(TopTalentEntity topTalentEntity) {	return new TopTalentData(topTalentEntity.getName());	}	}

起初,这段代码似乎没什么特别的问题;它提供了一个从 TopTalentEntity 实例检索出来的 TopTalentData 的 List。然而,仔细观察下,我们可以看到 TopTalentController 实际上在此做了些事情;也就是说,它将请求映射到特定端点,从数据库检索数据,并将从 TopTalentRepository 接收的实体转换为另一种格式。一个“更干净” 的解决方案是将这些关注点分离到他们自己的类中。看起来可能是这个样子的:

@RestController	
@RequestMapping("/toptal")	
@AllArgsConstructor	
public class TopTalentController {	private final TopTalentService topTalentService;	@RequestMapping("/get")	public List<TopTalentData> getTopTalent() {	return topTalentService.getTopTalent();	}	
}	@AllArgsConstructor	
@Service	
public class TopTalentService {	private final TopTalentRepository topTalentRepository;	private final TopTalentEntityConverter topTalentEntityConverter;	public List<TopTalentData> getTopTalent() {	return topTalentRepository.findAll()	.stream()	.map(topTalentEntityConverter::toResponse)	.collect(Collectors.toList());	}	
}	@Component	
public class TopTalentEntityConverter {	public TopTalentData toResponse(TopTalentEntity topTalentEntity) {	return new TopTalentData(topTalentEntity.getName());	}	
}

这种层次结构的另一个优点是,它允许我们通过检查类名来确定将功能驻留在何处。此外,在测试期间,如果需要,我们可以很容易地用模拟实现来替换任何类。

错误四:缺乏异常处理或处理不当

一致性的主题并非是 Spring(或 Java)所独有的,但仍然是处理 Spring 项目时需要考虑的一个重要方面。虽然编码风格可能存在争议(通常团队或整个公司内部已达成一致),但拥有一个共同的标准最终会极大地提高生产力。对多人团队尤为如此;一致性允许交流发生,而不需要花费很多资源在手把手交接上,也不需要就不同类的职责提供冗长的解释。

考虑一个包含各种配置文件、服务和控制器的 Spring 项目。在命名时保持语义上的一致性,可以创建一个易于搜索的结构,任何新的开发人员都可以按照自己的方式管理代码;例如,将 Config 后缀添加到配置类,服务层以 Service 结尾,以及控制器用 Controller 结尾。

与一致性主题密切相关,服务器端的错误处理值得特别强调。如果你曾经不得不处理编写很差的 API 的异常响应,那你可能知道原因 —— 正确解析异常会是一件痛苦的事情,而确定这些异常最初发生的原因则更为痛苦。

作为一名 API 开发者,理想情况下你希望覆盖所有面向用户的端点,并将他们转换为常见的错误格式。这通常意味着有一个通用的错误代码和描述,而不是逃避解决问题:a) 返回一个 “500 Internal Server Error”信息。b) 直接返回异常的堆栈信息给用户。(实际上,这些都应该不惜一切代价地去避免,因为除了客户端难以处理以外,它还暴露了你的内部信息)。例如,常见错误响应格式可能长这样:

@Value	
public class ErrorResponse {	private Integer errorCode;	private String errorMessage;	}

与此类似的事情在大多数流行的 API 中也经常遇到,由于可以容易且系统地记录,效果往往很不错。将异常转换为这种格式可以通过向方法提供 @ExceptionHandler 注解来完成。

错误五:多线程处理不当

不管是桌面应用还是 Web 应用,无论是 Spring 还是 No Spring,多线程都是很难破解的。由并行执行程序所引起的问题是令人毛骨悚然且难以捉摸的,而且常常难以调试 —— 实际上,由于问题的本质,一旦你意识到你正在处理一个并行执行问题,你可能就不得不完全放弃调试器了,并 “手动” 检查代码,直到找到根本上的错误原因。

不幸的是,这类问题并没有千篇一律的解决方案;根据具体场景来评估情况,然后从你认为最好的角度来解决问题。当然,理想情况下,你也希望完全避免多线程错误。同样,不存在那种一刀切的方法,但这有一些调试和防止多线程错误的实际考虑因素:

  • 避免全局状态

首先,牢记 “全局状态” 问题。如果你正创建一个多线程应用,那么应该密切关注任何可能全局修改的内容,如果可能的话,将他们全部删掉。如果某个全局变量有必须保持可修改的原因,请仔细使用 synchronization,并对程序性能进行跟踪,以确定没有因为新引入的等待时间而导致系统性能降低。

  • 避免可变性

这点直接来自于 函数式编程,并且适用于 OOP,声明应该避免类和状态的改变。简而言之,这意味着放弃 setter 方法,并在所有模型类上拥有私有的 final 字段。它们的值唯一发生变化的时间是在构造期间。这样,你可以确定不会出现争用问题,且访问对象属性将始终提供正确的值。

  • 记录关键数据

评估你的程序可能会在何处发生异常,并预先记录所有关键数据。如果发生错误,你将很高兴可以得到信息说明收到了哪些请求,并可更好地了解你的应用程序为什么会出现错误。需要再次注意的是,日志记录引入了额外的文件 I/O,可能会严重影响应用的性能,因此请不要滥用日志。

  • 复用现存实现

每当你需要创建自己的线程时(例如:向不同的服务发出异步请求),复用现有的安全实现来代替创建自己的解决方案。这在很大程度上意味着要使用 ExecutorServices 和 Java 8 简洁的函数式 CompletableFutures 来创建线程。Spring 还允许通过 DeferredResult 类来进行异步请求处理。

错误六:不使用基于注解的验证

假设我们之前的 TopTalent 服务需要一个端点来添加新的 TopTalent。此外,假设基于某些原因,每个新名词都需要为 10 个字符长度。执行此操作的一种方法可能如下:

@RequestMapping("/put")	
public void addTopTalent(@RequestBody TopTalentData topTalentData) {	boolean nameNonExistentOrHasInvalidLength =	Optional.ofNullable(topTalentData)	.map(TopTalentData::getName)	.map(name -> name.length() == 10)	.orElse(true);	if (nameNonExistentOrInvalidLength) {	// throw some exception	}	topTalentService.addTopTalent(topTalentData);	
}

然而,上面的方法(除了构造很差以外)并不是一个真正 “干净” 的解决办法。我们正检查不止一种类型的有效性(即 TopTalentData 不得为空,TopTalentData.name 不得为空,且 TopTalentData.name 为 10 个字符长度),以及在数据无效时抛出异常。

通过在 Spring 中集成 Hibernate validator,数据校验可以更干净地进行。让我们首先重构 addTopTalent 方法来支持验证:

@RequestMapping("/put")	
public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) {	topTalentService.addTopTalent(topTalentData);	
}	@ExceptionHandler	
@ResponseStatus(HttpStatus.BAD_REQUEST)	
public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {	// handle validation exception	
}	
// 此外,我们还必须指出我们想要在 TopTalentData 类中验证什么属性:	
public class TopTalentData {	@Length(min = 10, max = 10)	@NotNull	private String name;	
}

现在,Spring 将在调用方法之前拦截其请求并对参数进行验证 —— 无需使用额外的手工测试。

另一种实现相同功能的方法是创建我们自己的注解。虽然你通常只在需要超出 Hibernate的内置约束集 时才使用自定义注解,本例中,我们假设 @Length 不存在。你可以创建两个额外的类来验证字符串长度,一个用于验证,一个用于对属性进行注解:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})	
@Retention(RetentionPolicy.RUNTIME)	
@Documented	
@Constraint(validatedBy = { MyAnnotationValidator.class })	
public @interface MyAnnotation {	String message() default "String length does not match expected";	Class<?>[] groups() default {};	Class<? extends Payload>[] payload() default {};	int value();	}	@Component	
public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> {	private int expectedLength;	@Override	public void initialize(MyAnnotation myAnnotation) {	this.expectedLength = myAnnotation.value();	}	@Override	public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {	return s == null || s.length() == this.expectedLength;	}	
}

请注意,这些情况下,关注点分离的最佳实践要求在属性为 null 时,将其标记为有效(isValid 方法中的 s == null),如果这是属性的附加要求,则使用 @NotNull 注解。

public class TopTalentData {	@MyAnnotation(value = 10)	@NotNull	private String name;	
}

错误七:(依旧)使用基于xml的配置

虽然之前版本的 Spring 需要 XML,但如今大部分配置均可通过 Java 代码或注解来完成;XML 配置只是作为附加的不必要的样板代码。

本文(及其附带的 GitHub 仓库)均使用注解来配置 Spring,Spring 知道应该连接哪些 Bean,因为待扫描的顶级包目录已在 @SpringBootApplication 复合注解中做了声明,如下所示:

@SpringBootApplication	
public class Application {	public static void main(String[] args) {	SpringApplication.run(Application.class, args);	}	
}

复合注解(可通过 Spring 文档 了解更多信息)只是向 Spring 提示应该扫描哪些包来检索 Bean。在我们的案例中,这意味着这个顶级包 (co.kukurin)将用于检索:

  • @Component (TopTalentConverter, MyAnnotationValidator)

  • @RestController (TopTalentController)

  • @Repository (TopTalentRepository)

  • @Service (TopTalentService) 类

如果我们有任何额外的 @Configuration 注解类,它们也会检查基于 Java 的配置。

错误八:忽略 profile

在服务端开发中,经常遇到的一个问题是区分不同的配置类型,通常是生产配置和开发配置。在每次从测试切换到部署应用程序时,不要手动替换各种配置项,更有效的方法是使用 profile。

考虑这么一种情况:你正在使用内存数据库进行本地开发,而在生产环境中使用 MySQL 数据库。本质上,这意味着你需要使用不同的 URL 和 (希望如此) 不同的凭证来访问这两者。让我们看看可以如何做到这两个不同的配置文件:

APPLICATION.YAML 文件

# set default profile to 'dev'	
spring.profiles.active: dev	# production database details	
spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal'	
spring.datasource.username: root	
spring.datasource.password:	
8.2. APPLICATION-DEV.YAML 文件	
spring.datasource.url: 'jdbc:h2:mem:'	
spring.datasource.platform: h2

假设你不希望在修改代码时意外地对生产数据库进行任何操作,因此将默认配置文件设为 dev 是很有意义的。

然后,在服务器上,你可以通过提供 -Dspring.profiles.active=prod 参数给 JVM 来手动覆盖配置文件。另外,还可将操作系统的环境变量设置为所需的默认 profile。

错误九:无法接受依赖项注入

正确使用 Spring 的依赖注入意味着允许其通过扫描所有必须的配置类来将所有对象连接在一起;这对于解耦关系非常有用,也使测试变得更为容易,而不是通过类之间的紧耦合来做这样的事情:


Misko Hevery 的 Google talk 深入解释了依赖注入的 “为什么”,所以,让我们看看它在实践中是如何使用的。在关注点分离(常见错误 #3)一节中,我们创建了一个服务和控制器类。

假设我们想在 TopTalentService 行为正确的前提下测试控制器。我们可以通过提供一个单独的配置类来插入一个模拟对象来代替实际的服务实现:

@Configuration	
public class SampleUnitTestConfig {	@Bean	public TopTalentService topTalentService() {	TopTalentService topTalentService = Mockito.mock(TopTalentService.class);	Mockito.when(topTalentService.getTopTalent()).thenReturn(	Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList()));	return topTalentService;	}	
}

然后,我们可以通过告诉 Spring 使用 SampleUnitTestConfig 作为它的配置类来注入模拟对象:

之后,我们就可以使用上下文配置将 Bean 注入到单元测试中。

错误十缺乏测试,或测试不当

尽管单元测试的概念已经存在很长时间了,但很多开发人员似乎要么 “忘记” 做这件事(特别是如果它不是 “必需” 的时候),要么只是在事后把它添加进来。这显然是不可取的,因为测试不仅应该验证代码的正确性,还应该作为程序在不同场景下应如何表现的文档。

在测试 Web 服务时,很少只进行 “纯” 单元测试,因为通过 HTTP 进行通信通常需要调用 Spring 的 DispatcherServlet,并查看当收到一个实际的 HttpServletRequest 时会发生什么(使它成为一个 “集成” 测试,处理验证、序列化等)。

REST Assured,一个用于简化测试REST服务的 Java DSL,在 MockMVC 之上,已经被证明提供了一个非常优雅的解决方案。考虑以下带有依赖项注入的代码片段:

@RunWith(SpringJUnit4Cla***unner.class)	
@ContextConfiguration(classes = {	Application.class,	SampleUnitTestConfig.class	
})	
public class RestAssuredTestDemonstration {	@Autowired	private TopTalentController topTalentController;	@Test	public void shouldGetMaryAndJoel() throws Exception {	// given	MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given()	.standaloneSetup(topTalentController);	// when	MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get");	// then	response.then().statusCode(200);	response.then().body("name", hasItems("Mary", "Joel"));	}	}

SampleUnitTestConfig 类将 TopTalentService 的模拟实现连接到 TopTalentController 中,而所有的其他类都是通过扫描应用类所在包的下级包目录来推断出的标准配置。RestAssuredMockMvc 只是用来设置一个轻量级环境,并向 /toptal/get 端点发送一个 GET 请求。

640?wx_fmt=png

640?wx_fmt=png

福利
扫描添加小编微信,备注“姓名+公司职位”,入驻【CSDN博客】,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!

640?wx_fmt=jpeg

推荐阅读:
  • 漫画:什么是希尔排序?
  • 一次失败的面试,复习一次一致性哈希算法
  • Pandas中第二好用的函数 | 优雅的Apply

  • 程序员因接外包坐牢 456 天!两万字揭露心酸经历
  • 限时早鸟票 | 2019 中国大数据技术大会(BDTC)超豪华盛宴抢先看

  • 阿里开源物联网操作系统 AliOS Things 3.0 发布,集成平头哥 AI 芯片架构!

  • 雷声大雨点小:Bakkt「见光死」了吗?

真香,朕在看了!

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

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

相关文章

confluence启动不起来_汽车“一键启动”只用来点火?太浪费!你不知道的还有这3个功能...

说到“一键启动”&#xff0c;相信大多车主都不陌生。在早些年&#xff0c;这个配置只有B级以上的车才会有&#xff0c;而现在普遍都具备了。那这个“一键启动”到底有什么作用呢&#xff1f;只能用来点火和无钥匙启动&#xff1f;不&#xff0c;还有这几点&#xff0c;我们一起…

数据可视化揭晓NBA球星顶薪背后的真相

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | pk哥责编 | 刘丹7月1日凌晨6点&#xff0c;2019年NBA自由市场正式拉开大幕。之后的故事&#xff0c;我们都知道了&#xff0c;很多球员都签了顶薪合同&#xff0c;在我们看来&#xff0c;这是一堆的天文数字。美媒统计了一个…

RabbitMQ的5种队列_简单队列_入门试炼_第4篇

Statement 文章目录一、队列总览1.1. 图示1.2. 说明二、简单队列2.1. 图示2.2. 导入RabbitMQ的客户端依赖2.3. 获取MQ的连接2.4. 生产者发送消息到队列2.5. 管理工具中查看消息2.6. 消费者从队列中获取消息一、队列总览 1.1. 图示 1.2. 说明 说明模式①简单队列②work模式 多…

阿里云操作审计 - 日志安全分析(一)

摘要&#xff1a; 阿里云操作审计ActionTrail审计日志已经与日志服务打通&#xff0c;提供准实时的审计分析、开箱机用的报表功能。本文介绍背景、配置和功能概览。背景安全形式与日志审计伴随着越来越多的企业采用信息化、云计算技术来提高效率与服务质量。针对企业组织的网络…

spark中local模式与cluster模式使用场景_不可不知的Spark调优点

不可不知的Spark调优点​mp.weixin.qq.com在利用Spark处理数据时&#xff0c;如果数据量不大&#xff0c;那么Spark的默认配置基本就能满足实际的业务场景。但是当数据量大的时候&#xff0c;就需要做一定的参数配置调整和优化&#xff0c;以保证业务的安全、稳定的运行。并且在…

命令逐行显示_Linux命令和Shell脚本学习随笔3

一、处理目录创建目录&#xff1a;mkdir Test创建多级目录: mkdir -p Test1/testsub删除目录&#xff1a;rmdir 该命令只允许删除空目录&#xff0c;删除非空命令提示&#xff1a;Directory not empty&#xff0c;如果文件夹下有文件可以配合命令rm进行删除二、查看文件内容命令…

【图解分布式架构】看不懂直接面壁

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 佚名责编 | 阿秃初始阶段架构初始阶段的小型系统、应用程序、数据库、文件等所有的资源都在一台服务器上。通俗称为LAMP。特征&#xff1a;应用程序、数据库、文件等所有的资源都在一台服务器上。描述&#xff1a;通常服务器…

RabbitMQ的5种队列_Work模式_入门试炼_第5篇

文章目录一、work模式1.1. 图示1.2. 消费者11.3. 消费者21.4. 生产者1.5. 测试二、Work模式的“能者多劳”2.1. 关键配置2.2. 测试&#xff1a;一、work模式 1.1. 图示 一个生产者、2个消费者。 一个消息只能被一个消费者获取。 1.2. 消费者1 package com.gblfy.rabbitmq.w…

阿里云云数据库RDS秒级监控功能解锁,通宵加班找故障将成为过去式

摘要&#xff1a; 每一个奋斗在前线的数据库管理员和运维人员似乎运气都不太好&#xff0c;这些人都绝对经历过的诡异事件就是&#xff1a;逢年过节必出故障&#xff0c;明明眼看着要休假了&#xff0c;又接到故障通知&#xff0c;只好通宵加班找问题。没问题的时候可能大家都不…

阿里云DDoS高防 - 访问与攻击日志实时分析(三)

摘要&#xff1a; 本文介绍了DDoS日志分析功能的日志报表的使用方法。概述本文介绍DDoS日志分析功能的日志报表的使用方法。前提配置刚进入DDoS高防控制台的全量日志下&#xff0c;在界面引导下开通日志服务并授权操作后。就可以给特定的网站启用日志分析功能了。报表界面介绍在…

vue获取输入框得内容_React入坑(四):获取输入框内的内容

方法一&#xff1a;通过event对象信息获取import React from react;class Show extends React.Component {//state 相当于是Vue里面的data state {text: "",//默认初始值 }; hanChange ev > {//onChange 为Change事件&#xff0c;ev是传递的参数 …

学计算机的用surface,11个高效利用Surface处理工作学习任务的方法 - Surface 使用教程...

在 Surface 上捕获屏幕截图同时按下“电源”按钮 “调高音量”按钮来捕获全屏幕的截图&#xff0c;然后即可将其粘贴到文档、电子邮件或应用中。 屏幕截图同时也会保存在“屏幕截图”文件夹中。在网页上做笔记在 Microsoft Edge 中&#xff0c;选择“设置及更多” >“更多工…

RabbitMQ的5种队列_消息确认模式_入门试炼_第6篇

消费者从队列中获取消息&#xff0c;服务端如何知道消息已经被消费呢&#xff1f; 模式1&#xff1a;自动确认 只要消息从队列中获取&#xff0c;无论消费者获取到消息后是否成功消息&#xff0c;都认为是消息已经成功消费。 模式2&#xff1a;手动确认 消费者从队列中获取消息…

5分钟了解什么是真正的深度学习

人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;在当下的火热程度我就不多说了&#xff0c;但是真正懂这方面的人又有多少呢&#xff1f;本文将带你了解人工智能和机器学习的基本知识。同时&#xff0c;你也会了解到机器学习中最火的方法——深度学习…

柱状图中xy轴怎么出现_如果制砂机设备在工作中出现堵料现象该怎么办?

制砂机设备作为砂石生产线中常用的设备&#xff0c;在整条生产线中起着举足轻重的作用&#xff0c;为我国的基础建设的发展提供了巨大的贡献。但是不管技术工艺如何不断的改革完善&#xff0c;在制砂机设备工作中还是会出现堵料的现象。当出现这种情况我们应该怎么办呢?下面我…

阿里云DDoS高防 - 访问与攻击日志实时分析(四)

摘要&#xff1a; 本文介绍了DDoS日志实时分析功能的费用计量细节与案例。概述本文介绍了DDoS日志的费用计量细节与案例。费用概述DDoS日志分析与报表功能依赖日志服务提供日志数据的实时查询与分析功能。当您开通DDoS访问与攻击日志实时分析功能时, 需要您根据界面提示开通日志…

c++检测输入是否为数字_Go64 for Mac(检测应用是否为64位)

“一种快速的方法来全面了解您的64位应用程序兼容性&#xff0c;并在安装macOS Catalina之前避免意外。”Go64 可以帮你检测出你的应用是否 64 位&#xff0c;以及软件版本号&#xff0c;开发商以及官网&#xff0c;甚至可以为你计算出软件升级所需要花费的金额。应用介绍从 ma…

阿里云DDoS高防 - 访问与攻击日志实时分析(二)

摘要&#xff1a; 本文介绍了如何配置DDoS日志分析功能&#xff0c;结合实际场景详细介绍了如何使用日志对DDoS访问与攻击日志进行分析与图形化操作。概述本文介绍了如何配置DDoS日志分析功能&#xff0c;结合实际场景详细介绍了如何使用日志对DDoS访问与攻击日志进行分析与图形…

【面试妥了】史上最全Spark面试题

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 游骑小兵责编 | 阿秃Spark问题精华Q&#xff1a;什么是Spark&#xff1f;A&#xff1a;简单理解&#xff0c;Spark是在Hadoop基础上的改进&#xff0c;是UC Berkeley AMP lab所开源的类Hadoop MapReduce的通用的并行计算框架…

RabbitMQ的5种队列_订阅模式_入门试炼_第7篇

解读&#xff1a; 1、1个生产者&#xff0c;多个消费者 2、每一个消费者都有自己的一个队列 3、生产者没有将消息直接发送到队列&#xff0c;而是发送到了交换机 4、每个队列都要绑定到交换机 5、生产者发送的消息&#xff0c;经过交换机&#xff0c;到达队列&#xff0c;实现&…