文章目录
一.Echarts入门
二.使用Echarts
1.创建模块
- 使用dobbo框架 需要创建stat_service 和stat_interface
- stat_service是一个web工程,在这里面需要配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 监听器监听其他的spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
- 配置 applicationContext-dubbo.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--dubbo的应用名称,一般我们都使用项目名称-->
<dubbo:application name="export_stat_service">
<!--运维相关-->
<dubbo:parameter key="qos.enable" value="false"></dubbo:parameter>
</dubbo:application>
<!--注册中心,本机的2181端口-->
<dubbo:registry address="zookeeper://127.0.0.1:2181" ></dubbo:registry>
<!--非常重要,dubbo提供的访问端口号,8080tomcat容器仅仅是启动项目,真正的访问是这个端口20881-->
<dubbo:protocol name="dubbo" port="20883"></dubbo:protocol>
<!--注解支持,dubbo的包扫描-->
<dubbo:annotation package="cn.itcast.service"></dubbo:annotation>
</beans>
- 编写接口和实现类
package cn.itcast.service;
import java.util.List;
import java.util.Map;
public interface StatService {
List<Map> getFactoryData(String companyId);
List<Map> getOnlineData(String companyId);
List<Map> getSellData(String companyId);
}
- 编写dao和xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.dao.stat.StatDao">
<select id="getFactoryData" parameterType="String" resultType="map">
SELECT
factory_name as name ,
SUM(amount) as value
FROM
co_contract_product
WHERE
company_id = #{companyId}
GROUP BY
factory_name
ORDER BY SUM(amount) DESC
</select>
<select id="getOnlineData" parameterType="String">
SELECT
product_no as name ,
SUM(amount) as value
FROM
co_contract_product
WHERE
company_id = #{companyId}
GROUP BY
product_no
ORDER BY SUM(amount) DESC
LIMIT 15
</select>
<select id="getSellData" parameterType="String">
SELECT
st_online_info.A1 as name,
a.value as value
FROM
st_online_info
LEFT JOIN (
SELECT
DATE_FORMAT(TIME, '%H') AS name,
COUNT(*) AS value
FROM
st_sys_log
WHERE
st_sys_log.company_id = #{companyId}
GROUP BY
DATE_FORMAT(TIME, '%H')
) a ON a.name=st_online_info.A1
</select>
</mapper>
- 编写controller
package cn.itcast.web.controller.stat;
import cn.itcast.service.StatService;
import cn.itcast.web.controller.BaseController;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
/**
* @author cyy
* @date 2020/3/22 18:43
*/
@RequestMapping(value = "/stat")
@Controller
public class StatController extends BaseController {
@Reference
private StatService statService;
// 跳转到统计分析页面
@RequestMapping(value = "/toCharts")
public String toCharts(String chartsType){
return "stat/stat-"+chartsType;
}
@RequestMapping(value = "/getFactoryData")
@ResponseBody
public List<Map> getFactoryData(){
return statService.getFactoryData(companyId);
}
@RequestMapping(value = "/getOnlineData")
@ResponseBody
public List<Map> getOnlineData(){
return statService.getOnlineData(companyId);
}
@RequestMapping(value = "/getSellData")
@ResponseBody
public List<Map> getSellData(){
return statService.getSellData(companyId);
}
}
2. 前台页面访问
3. 根据 官网可进行样式的更换
// 指定图表的配置项和数据
option = {
xAxis: {
type: 'category',
data: titles
},
yAxis: {
type: 'value'
},
series: [{
data: values,
type: 'line'
}]
};
option = {
xAxis: {
type: 'category',
data: titles
},
yAxis: {
type: 'value'
},
series: [{
data:values,
type: 'line',
smooth: true
}]
};
三.邮件发送
1.编写工具类
package cn.itcast.common.utils;
import javax.mail.Address;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
public class MailUtil {
//实现邮件发送的方法
public static void sendMsg(String to ,String subject ,String content) throws Exception{
//1. 邮件服务设置
Properties props = new Properties();
props.setProperty("mail.smtp.host", "smtp.163.com"); //设置主机地址 smtp.qq.com smtp.sina.com
props.setProperty("mail.smtp.auth", "true");//认证
//2.产生一个用于邮件发送的Session对象
Session session = Session.getInstance(props);
//3.产生一个邮件的消息对象
MimeMessage message = new MimeMessage(session);
//4.设置消息的发送者
Address fromAddr = new InternetAddress("邮箱");
message.setFrom(fromAddr);
//5.设置消息的接收者
Address toAddr = new InternetAddress(to);
//TO 直接发送 CC抄送 BCC密送
message.setRecipient(MimeMessage.RecipientType.TO, toAddr);
//6.设置主题
message.setSubject(subject);
//7.设置正文
message.setText(content);
//8.准备发送,得到火箭
Transport transport = session.getTransport("smtp");
//9.设置火箭的发射目标
transport.connect("smtp.sina.com", "邮箱", "授权码");
//10.发送
transport.sendMessage(message, message.getAllRecipients());
//11.关闭
transport.close();
}
}
2.在注册的时候给用户发送一封邮件
@RequestMapping(value = "/edit", name = "保存用户信息")
public String edit(User user){
//在实体类中设置企业信息
user.setCompanyId(companyId);
user.setCompanyName(companyName);
//1、判断user.getId,是新建还是编辑
if (StringUtil.isEmpty(user.getId())){
//2、调用userService新建
user.setId(UUID.randomUUID().toString());
// !!**new 新增用户时,获取用户的密码**
String password=user.getPassword();
user.setPassword(Encrypt.md5(user.getPassword(), user.getEmail()));
userService.save(user);
// !! **new 新增用户时,需要给用户发送一封邮件**
String to =user.getEmail();
String subject="主题 ";
String content ="用户名"+to+"密码为"+password;
MailUtil.sendMsg(to,subject,content);
}else {
//3、调用userService编辑
userService.update(user);
}
//4、重定向到list.do
return "redirect: /system/user/list.do";
}
3.发送邮件存在问题
- 邮件发送过慢
- 解决:异步拆分为两个程序,采用队列的方式进行监听
四.消息中间件
1.了解消息中间件
- 消息中间件是分布式系统中最重要的组件,主要解决应用解耦,异步消息,流量削峰
2.MQ的两种模式
- 队列模式
- 普通队列:一个提供者发送每条消息,都有一个消费者进行接收
- work模式:一个提供者发送多条消息,有多个消费者进行接收,但是每个消费者只能接收到一条消息,消费者之间消息不能重复(轮询机制发送)
- 订阅模式
- fanout模式:一个提供者发送多条消息,同时有多个消费者收到同样的消息
- direct模式:增加一个关键字,一个提供者发送多条消息时,里面包含关键字,多个消费者根据关键字进行过滤,匹配成功的接收到消息
- topic模式:增加一个通配符,一个提供者发送多条消息时,里面包含关键字,多个消费者根据通配符进行过滤,匹配成功的接收到消息
五.安装RabbitMq
1.第一步:下载并安装erlang
- 需要配置环境变量
变量名:ERLANG_HOME
变量值就是刚才erlang的安装地址,点击确定。
点击“新建”,将%ERLANG_HOME%\bin加入到path中。
- cmd 输入erl,看到版本号就说明erlang安装成功了。
2.下载并安装RabbitMQ
- 安装rabbitMQ-server
- RabbitMQ安装好后接下来安装RabbitMQ-Plugins。打开命令行cd,输入RabbitMQ的sbin目录。
然后在后面输入rabbitmq-plugins enable rabbitmq_management命令进行安装
- 在cmd 运行 倒数第二个
- 访问http://localhost:15672 默认用户名和密码都是guest
3.安装过程中遇到的问题
- 无法访问15672 提示节点已经在本机存在
- 解决:在服务中关掉进程,重启计算机,然后在cmd重新执行rabbitmq-server.bat 登陆成功
六.RabbitMQ后台操作
1.添加一个用户
2.添加一个虚拟主机
3.MQ端口号
七.RabbitMQ队列模式
1.队列模式–普通队列(手工回执)
应用场景:并发量不是很大,比如:新增用户发邮件
//6、消费队列
channel.basicConsume("heima123_queue", true, new MyConsumer(channel));
第二个参数代表自动回执,拿到这条消息之后,不管是否消费成功,都进行回执。
回执指的是告诉RabbitMQ,消息已经被消费了,所以消息在RabbitMQ服务里物理删除了。
问题:如果消费者拿到消息之后,处理业务时失败了,发生异常,这条消息就永久消失了。
所以一般第二个参数会设置成false,需要手工进行回执。
用到的场景:消费者在处理完业务时,成功之后才手工回执,如果失败则不用回执。
//确认收到一个或多个消息
channel.basicAck(envelope.getDeliveryTag() , false);
2.队列模式–Work模式队列
应用场景:并发量也不是很大,消息消费者需要大量的时间去运行。
消息提供者提供一条消息只需要2秒,消息消费者需要1小时运行。效率就非常低,需要有多个消息消息者进行处理。
举例:前台页面(查询去年双11到今年双11整年的销量):【准备数据】【查询数据】
。
2.1两个一模一样的消费者
两个一模一样的消费者,每个消费者轮流获取消息,消息并不重复。
2.2一个消费者处理慢一个消费者处理快
应用场景:多个消费者放在多台电脑上,有的电脑快,有的电脑慢。
生产者生产100条消息,消费者1和消费者2还是轮流获取消息,快的会先处理完,慢的处理的非常慢,获取的消息和两个一模一样的消费者获取的消息是一样的,差异在于处理的速度不同。
2.3每次取消息只取一条
在两个消费者上面设置每次只获取一条消息
//设置每次取的数量
channel.basicQos(1);
八.RabbitMQ订阅模式
1.订阅模式–fanout
应用场景:会员提交订单之后,需要处理几件事:
1、付款;2、保存订单,写入订单表;3、减少库存,写入库存表;4、通知物流,写入物流表;5、通知客服,写入客服表;6、通知营销,写入促销表;7、通知售后,写入安装表;8、通知厂家,厂家发货表
消息生产者声明交换机
// 声明exchange
//参数1: 交换机名称
//参数2: 交换机类型
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
消息消费者绑定交换机
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
2.订阅模式–direct
// 声明exchange
//参数1: 交换机名称
//参数2: 交换机类型
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
消息生产者指定关键字
channel.basicPublish(EXCHANGE_NAME, "back", null, message.getBytes());
消息消费者指定关键字
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "buy");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "back");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "ask");
3.订阅模式–topic
生产者发送消息
channel.basicPublish(EXCHANGE_NAME, "ask.phone", null, message.getBytes());
消费者绑定交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "ask.*");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "*.*");
九.通过RabbitMQ发送邮件(重点)
1.消息的生产者
1.1 引入依赖
<!--rabbit整合spring-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
1.2 applicationContext-mq.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:contact="http://www.springframework.org/schema/context"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-2.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--包扫描-->
<context:component-scan base-package="cn.itcast.web"></context:component-scan>
<!-- 公共部分 -->
<!-- 创建连接类 连接安装好的 rabbitmq -->
<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<property name="virtualHost" value="heima111"></property>
<!-- username,访问RabbitMQ服务器的账户,默认是guest -->
<property name="username" value="admin" />
<!-- username,访问RabbitMQ服务器的密码,默认是guest -->
<property name="password" value="admin" />
<!-- host,RabbitMQ服务器地址,默认值"localhost" -->
<property name="host" value="127.0.0.1" />
<!-- port,RabbitMQ服务端口,默认值为5672 -->
<property name="port" value="5672" />
</bean>
<rabbit:admin connection-factory="connectionFactory"/>
<!--解析json , MQ会自动将数据转换成json字符串-->
<bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
<!--模版对象 用于发送消息-->
<rabbit:template id="amqpTemplate" exchange="test-mq-exchange" connection-factory="connectionFactory" message-converter="jackson2JsonMessageConverter" />
<!--配置队列 durable="true" 表示消息需要持久化 即服务器崩溃会数据不会丢失 auto-delete="false" 表示服务器停止后数据是否自动删除 exclusive:表示该消息队列是否只在当前connection生效。默认false。 -->
<rabbit:queue id="test_queue_key2" name="mail.send" durable="true" auto-delete="false" exclusive="false" />
<!--交换机
定义消息队列,durable:是否持久化,如果想在RabbitMQ退出或崩溃的时候,不会失去所有的queue和消息,需要同时标志队列(queue)和交换机(exchange)是持久化的,即rabbit:queue标签和rabbit:direct-exchange中的durable=true,而消息(message)默认是持久化的可以看类org.springframework.amqp.core.MessageProperties中的属性public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;exclusive: 仅创建者可以使用的私有队列,断开后自动删除;auto_delete: 当所有消费客户端连接断开后,是否自动删除队列
绑定队列,rabbitmq的exchangeType常用的三种模式:direct,fanout,topic三种,我们用direct模式,即rabbit:direct-exchange标签,Direct交换器很简单,如果是Direct类型,就会将消息中的RoutingKey与该Exchange关联的所有Binding中的BindingKey进行比较,如果相等,则发送到该Binding对应的Queue中。有一个需要注意的地方:如果找不到指定的exchange,就会报错。但routing key找不到的话,不会报错,这条消息会直接丢失,所以此处要小心,auto-delete:自动删除,如果为Yes,则该交换机所有队列queue删除后,自动删除交换机,默认为false
-->
<rabbit:direct-exchange name="test-mq-exchange" durable="true" auto-delete="false" id="test-mq-exchange">
<rabbit:bindings>
<!--绑定队列 队列名称为mail.send-->
<rabbit:binding queue="test_queue_key2" key="mail.send"/>
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
1.3 改写userController
// 1、注入一个AmqpTemplate在方法上面注入
@Autowired
private AmqpTemplate amqpTemplate;
// 2、调用amqpTemplate.convertAndSend
Map map = new HashMap();
map.put("to",to);
map.put("subject",subject);
map.put("content",content);
//将邮件的内容发送消息
//两个参数:
//1、队列名称
//2、消息内容
amqpTemplate.convertAndSend("mail.send",map);
2.消息的消费者
2.1 引入依赖
解析发送的内容
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
2.2 applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:contact="http://www.springframework.org/schema/context"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-2.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--连接工厂-->
<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<!--<constructor-arg value="${mq.vhost}" />-->
<property name="virtualHost" value="heima123"></property>
<!-- username,访问RabbitMQ服务器的账户,默认是guest -->
<property name="username" value="admin" />
<!-- username,访问RabbitMQ服务器的密码,默认是guest -->
<property name="password" value="admin" />
<!-- host,RabbitMQ服务器地址,默认值"localhost" -->
<property name="host" value="127.0.0.1" />
<!-- port,RabbitMQ服务端口,默认值为5672 -->
<property name="port" value="5672" />
</bean>
<rabbit:admin connection-factory="connectionFactory" />
<rabbit:queue name="mail.send" auto-declare="true" durable="true" />
<!-- 消费者部分 -->
<!-- 自定义接口类 -->
<bean id="myListener" class="cn.itcast.mail.MyListener"></bean>
<!--解析json-->
<bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
<!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20">
<rabbit:listener queues="mail.send" ref="myListener" />
</rabbit:listener-container>
</beans>
2.3建一个监听类
cn.itcast.mail.MyListener
package cn.itcast.mail;
import cn.itcast.common.utils.MailUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import java.util.Map;
/**
* @author cyy
* @date 2020/3/26 10:38
*/
public class MyListener implements MessageListener {
// 监听消息
public void onMessage(Message message) {
String msg = new String(message.getBody());
/* System.out.println("msg = " + msg);*/
Map map = JSONObject.parseObject(msg,Map.class);
String to = String.valueOf(map.get("to"));
String subject= String.valueOf(map.get("subject"));
String content = String.valueOf(map.get("content"));
System.out.println(to+"====="+subject+"====="+content);
try {
MailUtil.sendMsg(to,subject,content);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4 建一个启动类
package cn.itcast.run;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author cyy
* @date 2020/3/26 10:43
*/
public class MyApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
}
}