
A苏弋
2022/10/31阅读:19主题:默认主题
SpringBoot:快速入门(续)
SpringBoot:快速入门(续)
异步任务:
目录结构

helloController:
package com.sec.controller;
import com.sec.service.helloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAsync//开启异步任务
public class helloController {
@Autowired
helloService hs;
@RequestMapping("/hello")
public String test(){
hs.hello();//停止三秒
return "ok";
}
}
helloService:
package com.sec.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class helloService {
@Async//告诉spring 这是一个异步方法
public void hello(){
try {
Thread.sleep(3000);//睡眠三秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据正在处理中");
}
}
==注意:==
-
首先要给方法前面写上注解@Async,告诉spring这是一个异步方法 -
然后要在controller类前开启异步任务 @EnableAsync
邮件发送:
-
导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency> -
编写代码
package com.sec;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Random;
@SpringBootTest
class SpringBoot09AsyncApplicationTests {
@Autowired
JavaMailSenderImpl jsi;
@Test
//一个简单的邮件发送
void contextLoads() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setSubject("郝嘉诚你好啊!");
mailMessage.setText("感谢你的代码");
mailMessage.setFrom("990784805@qq.com");
mailMessage.setTo("990784805@qq.com");
jsi.send(mailMessage);
}
@Test
//一个复杂的邮件发送
void contextLoads1() throws MessagingException {
MimeMessage mimeMessage = jsi.createMimeMessage();
//组装
MimeMessageHelper messageHelper=new MimeMessageHelper(mimeMessage,true);
//正文
messageHelper.setSubject("尊敬的苏弋您好!");
Random random = new Random();
int num = random.nextInt(9000) + 1000;
messageHelper.setText("<p style='color:red'>您的验证码是:"+num+"</p>",true);
//附件
messageHelper.addAttachment("1.jpg",new File("C:\\Users\\飞驰的苏弋\\Pictures\\Camera Roll\\1.jpg"));
messageHelper.addAttachment("2.jpg",new File("C:\\Users\\飞驰的苏弋\\Pictures\\Camera Roll\\2.jpg"));
//发送
messageHelper.setFrom("990784805@qq.com");
messageHelper.setTo("3046865110@qq.com");
jsi.send(mimeMessage);
}
//把发送邮件的代码编写成了一个工具,可调用
public void sendMail(Boolean html,String title,String text,Boolean textHtml,String sendFrom,String sendTo,String fileName,File file) throws MessagingException{
MimeMessage mimeMessage = jsi.createMimeMessage();
//组装
MimeMessageHelper messageHelper=new MimeMessageHelper(mimeMessage,html);
//正文
messageHelper.setSubject(title);
messageHelper.setText(text,textHtml);
//附件
messageHelper.addAttachment(fileName,file);
//发送
messageHelper.setFrom(sendFrom);
messageHelper.setTo(sendTo);
jsi.send(mimeMessage);
}
}
工具类调用法:
-
发送邮件工具类:sendMail.java
package com.sec.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Random;
@Service
public class sendMail {
@Autowired
JavaMailSenderImpl jsi;
public void sendMail(Boolean html, String title, String text, Boolean textHtml, String sendFrom, String sendTo, String fileName, File file) throws MessagingException {
MimeMessage mimeMessage = jsi.createMimeMessage();
//组装
MimeMessageHelper messageHelper=new MimeMessageHelper(mimeMessage,html);
//正文
messageHelper.setSubject(title);
messageHelper.setText(text,textHtml);
//附件
messageHelper.addAttachment(fileName,file);
//发送
messageHelper.setFrom(sendFrom);
messageHelper.setTo(sendTo);
jsi.send(mimeMessage);
}
public void hjc() throws MessagingException {
MimeMessage mimeMessage = jsi.createMimeMessage();
//组装
MimeMessageHelper messageHelper=new MimeMessageHelper(mimeMessage,true);
//正文
messageHelper.setSubject("尊敬的苏弋您好!");
Random random = new Random();
int num = random.nextInt(9000) + 1000;
messageHelper.setText("<p style='color:red'>您的验证码是:"+num+"</p>",true);
//附件
messageHelper.addAttachment("1.jpg",new File("C:\\Users\\飞驰的苏弋\\Pictures\\Camera Roll\\1.jpg"));
messageHelper.addAttachment("2.jpg",new File("C:\\Users\\飞驰的苏弋\\Pictures\\Camera Roll\\2.jpg"));
//发送
messageHelper.setFrom("990784805@qq.com");
messageHelper.setTo("3046865110@qq.com");
jsi.send(mimeMessage);
}
}
-
controller层调用:
package com.sec.controller;
import com.sec.service.helloService;
import com.sec.service.sendMail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpSession;
import java.io.File;
@Controller
@EnableAsync//开启异步任务
public class helloController {
@Autowired
helloService hs;
@Autowired
sendMail sm;
@RequestMapping({"/","/index","/index.html"})
public String index(){
return "/index";
}
@RequestMapping("/hello")
@ResponseBody
public String test(){
hs.hello();//停止三秒
return "ok";
}
@RequestMapping("/sendMail")
public String sendMail(Model model) throws MessagingException {
sm.sendMail(true,"飞驰的苏弋你好!","<h2 style='color:red'>你好呀飞驰的苏弋</h2>",true,"990784805@qq.com","3046865110@qq.com","5.png",new File("C:\\Users\\飞驰的苏弋\\Pictures\\Camera Roll\\5.png"));
model.addAttribute("msg","发送成功");
return "/index";
}
} -
index.html页面编写一个按钮 结合Thymeleaf
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<p th:text="${msg}"></p>
</div>
<a th:href="@{/sendMail}">点击发送邮箱</a>
</body>
</html>
定时任务:
TaskScheduler 任务调度者
TaskExecutor 任务执行者
@EnableScheduling //开启定时功能的注解
@Scheduled //什么时候执行
-
开启定时功能
@EnableAsync//开启异步任务
@EnableScheduling//开启定时功能的注解
@SpringBootApplication
public class SpringBoot09AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot09AsyncApplication.class, args);
}
} -
编写定时任务的类 cron表达式:是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义
corn从左到右(用空格隔开):==秒 分 时 日 月 周几 年==
==注意事项:==
每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:
(1)✳:表示匹配该域的任意值。假如在Minutes域使用✳, 即表示每分钟都会触发事件。
(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用,如果使用表示不管星期几都会触发,实际上并不是这样。
(3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
(4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.
(5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
(6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。
(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
@Service
public class ScheduledService {
//在一个特定的时间执行这个方法 Timer
//cron表达式
//秒 分 时 日 月 周几
@Scheduled(cron = "59 7 19 * * ?")//每天的下午7点07分59秒执行
public void hello(){
System.out.println("hello,你被执行了");
}
}
RPC
-
简单的说,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。 -
RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯) -
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式) -
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

分布式Dubbo + Zookeeper + SpringBoot
什么是dubbo?
Apache Dubbo 是一款高性能、轻量级的开源java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
dubbo的好处
-
透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。 -
软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。 -
服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。
dubbo基本概念

服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
调用关系说明
服务容器负责启动,加载,运行服务提供者。
服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者在启动时,向注册中心订阅自己所需的服务。
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
zookeeper
ZooKeeper是一个经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调服务。 分布式应用程序可以基于ZooKeeper实现数据发布与订阅、负载均衡、命名服务、分布式协调与通知、集群管理、Leader选举、分布式锁、分布式队列等功能。
dubbo-admin安装测试
-
下载dubbo-admin 地址:https://github.com/apache/dubbo-admin/tree/master
-
解压进入目录 修改dubbo-admin\src\main\resource\application.properties 指定zookeeper地址
server.port=7001 spring.velocity.cache=false spring.velocity.charset=UTF-8 spring.velocity.layout-url=/templates/default.vm spring.messages.fallback-to-system-locale=false spring.messages.basename=i18n/message spring.root.password=root spring.guest.password=guest dubbo.registry.address=zookeeper://127.0.0.1:2181
-
在项目目录下打包dubbo-admin
mvn clean package -Dmaven.test.skip=true
第一次打包比较慢,耐心等待
-
执行dubbo-admin\target下的dubbo-admin-SNAPSHOT.jar
java -jar dubbo-admin-SNAPSHOT.jar
【注意:zookeeper的服务一定要打开】 执行完毕后,我们去访问http://localhost:7001/ ,这时候我们需要输入账户和密码,我们都是默认的root-root; 登陆成功后,查看界面 zookeeper:是一个注册中心 dubbo-admin:是一个监控管理后台,查看我们注册了那些服务,消费了哪些服务 Dubbo:jar包
安装完成! SpringBoot+Dubbo+zookeeper
服务注册发现实战
导入依赖
<!--导入依赖 Dubbo zookeeper-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!--日志会冲突-->
<!--引入zookeeper-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<!--排除这个slf4j-log4j12-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
框架搭建
-
启动zookeeper!
-
IDEA创建一个空项目;
-
创建一个模块,实现服务提供者:provider-server,选择web依赖即可;
-
项目创建完毕,我们写一个服务,比如卖票的服务;
编写接口
package com.sec.service;
public interface TicketService {
public String getTicket();
}
编写实现类
provider-server提供的服务添加注解 ==这里@service不要导错包==
package com.sec.service;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
@Service //可以被扫描到,在项目一启动就自动注册到注册中心
@Component //使用了dubbo后尽量不要用service注解
public class TicketServiceImpl implements TicketService{
@Override
public String getTicket() {
return "<苏弋>";
}
}
provider-server的==application.properties:==
server.port=8001
#服务应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#哪些服务要被注册
dubbo.scan.base-packages=com.sec.service
再创建一个模块,实现消费者:consumer-server,选择web依赖;
编写类,再把TicketService接口拿到这边的service包下
package com.sec.service;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//想拿到provider-server提供的票 要去注册中心拿到服务
@Reference //引用 Pom坐标,可以定义路径相同的接口名
TicketService ticketService;
public String buyTicket(){
String ticket=ticketService.getTicket();
System.out.println("在注册中心拿到=>"+ticket);
return "在注册中心拿到=》"+ticket;
}
}
consumer-server的==application.properties==
server.port=8002
#消费去哪里拿服务需要暴露自己的名字
dubbo.application.name=consumer-server
#注册中心的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
controller调用buyTicket方法
package com.sec.controller;
import com.sec.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class testController {
@Autowired
UserService userService;
@RequestMapping("/test")
@ResponseBody
public String test(){
String str = userService.buyTicket();
return str;
}
}
代码编写完毕;
测试运行
-
开启zookeeper服务,找到zkServer.cmd,双击运行。
-
到dubbo-admin-0.0.1-SNAPSHOT.jar的目录下,cmd运行 java -jar dubbo-admin-0.0.1-SNAPSHOT.jar。
-
开启提供者服务
-
访问http://localhost:7001 查看提供者服务有没有
-
访问http://localhost:8002/test
-
查看后台Dubbo Admin的消费者是否产生
1620440287811 -
完毕
总结步骤:
前提:zookeeper服务已开启
-
提供者提供服务 -
导入依赖 -
配置注册中心的地址,以及服务发现名,和要扫描的包 -
在想要被注册的服务上面增加一个注解==@Service== dubbo的注解
-
-
消费者如何消费 -
导入依赖 -
配置注册中心的地址,配置自己的服务名 -
从远程注入服务 @Reference
-
作者介绍
