0%

文章字数:559,阅读全文大约需要2分钟

MQTT是一个互联网协议,基于这个协议有很多的实现。

一、MQTT Broker的需求

1.1 基本需求

  1. 支持mqtt3.1 / mqtt3.1.1可选mqtt5.0协议
  • 3.13.1.1是常见的协议版本,几乎生产的IOT设备都支持,5.0普及可能还要一段时间。
  1. 支持QOS0QOS1可选QOS2
  • 各大厂商至少都支持了QOS1,能够保证信息的必到达。
  1. 支持遗嘱信息:即设备异常断开后通知后端服务或者其他设备处理

  2. 支持持久化:比如QOS1的消息,设备因为网络原因接收不到或者异常断线,需要把未发送的信息保存在session中。

  3. 支持多种连接方式

  • MQTT over TCP: 最基本的连接方式
  • MQTT over Webscoket: 基于Websocket连接的MQTT
  • MQTT over TCP/SSL: 基础连接之上做了加密,一般采用TLS加密
  • MQTT over Websocket/SSL: 基于Websocket的加密连接,一般SSL采用TLS
  1. 集群
  • 需要海量的连接需要支持集群,集群的session持久化和集群通信都是难点。
  • 集群信息共享,可以保证某个节点宕机时功能还能正常使用,但是未发送的信息可能会丢失。
  1. 自定义权限验证
  • 连接阶段是否允许连接
  • 发送阶段判断是否允许发送
  • 订阅阶段是否允许订阅
  • 大部分开源Broker只支持userNamePassword的验证。ClientIdIp不一定支持

1.2 可选需求

  1. 保留信息
  • 保留信息就是主题会依照QOS级别保留最后一条消息,当有新的订阅时会发送这条消息
  • 主要应对订阅之后不知道第一条信息何时会发送的情况。如获取设备状态,设备如果不发送信息新加进来的管理端就无法获得状态信息,不知道如何显示。如果这个时候有一条保留信息,就能够知道当前设备状态。
  • 开销较大,每次订阅主题都需要检查有没有保留消息。

文章字数:173,阅读全文大约需要1分钟

Math类包含很多数学计算常用函数

算数计算

  • Math.sqrt(v): 计算v的平方根
  • Math.cbrt(v): 计算v的立方根
  • Math.pow(a, b): 计算a的b次方
  • Math.max(a, b): 返回a和b中大的值
  • Math.min(a, b): 返回a和b中小的值
  • Math.abs(v): 计算v的绝对值

小数取整

  • Math.ceil(v): 向上取整(正数整数部分加1,负数去掉小数部分)
  • Math.floor(v): 向下取整(正数去掉小数部分,负数整数部分加1)
  • Math.rint(v): 四舍五入(当小数是5的时候,优先取偶数。即两边距离一样是先去偶数,如12.5=>12
  • Math.round(v): 四舍五入

随机数

  • Math.random(): 取一个[0, 1)之间的值

文章字数:162,阅读全文大约需要1分钟

今天新建了一个项目,测试demo的时候发现返回界面的Controller报404

分析

  1. 打断点,发现进入了Controller。并且没有抛出异常,说明基本流程是对的。
  2. ModelAndView有数据,但是返回后404.可能是没找到视图
  3. 查看配置文件
    1
    2
    spring.freemarker.suffix=.xx
    spring.freemarker.template-loader-path=classpath:/static
    查看文件名后缀是否是设置的.xx(modelAndView中不要加后缀)
    查看路径是否是设置的根目录下的(static/xxx)

我的问题是没有static路径,创建static目录,把文件放进去。问题解决

其它可能

  1. @RestController或者@RequestMapping使用错误

文章字数:1042,阅读全文大约需要4分钟

锁分类

按照粒度可以分为表级锁table-level locking和行级锁row-level locking

  • 表级锁:开销小,加锁快。但是因为锁定的粒度大,发生冲突的概率高,支持并发度低。适合查询为主的应用

  • 行级锁:开销大,加锁慢。但是粒度小,发生冲突概率低,支持并发高。

InnoDB默认采用行级锁MyISAMMEMORY默认采用表级锁

行锁

InnoDB实现类两种类型的行锁

  • 共享锁S:获取该锁的事务可以读取数据,并同时允许其他事务获取改行的共享锁,但阻止其他事务获取排他锁。

  • 排他锁X:获取该锁的事务可以更新或删除数据行,同时阻止其他事务获取该行的共享锁和排他锁。

锁类型 共享锁S 排他锁X
共享锁S 兼容 冲突
排他锁X 冲突 冲突

手动添加行锁

默认情况下锁都是自动获取的,但也可以手动加锁。

  • select * from table where id = 1 for share
    for share就是加共享锁,MySQL8.0之前的版本可以使用select...lock in share mode
    由于 InnoDB 中的自动提交 autocommit 默认设置为 ON,我们必须在事务中为数据行加锁;或者将 autocommit 设置为 OFF。

  • select * from table where id = 1 for update
    for update是获取排他锁
    数据行被其它事务加了共享锁,并且事务未结束(即未释放)此时其它事务加排他锁则会处于等待状态,直到超时。(因为不兼容)

意向锁

为了兼容表级锁和行级锁,InnoDB采用了意向锁(属于表级锁)。意向锁为自动添加,也可以手动LOCK TABLE添加

  • 意向共享锁(IS):事务在给数据行加行级共享锁之前,必须先取得该表的 IS 锁。
  • 意向排他锁(IX):事务在给数据行加行级排他锁之前,必须先取得该表的 IX 锁。
锁类型 共享锁S 排他锁X 意向共享锁IS 意向排他锁
共享锁S 兼容 冲突 兼容 冲突
排他锁X 冲突 冲突 冲突 冲突
意向共享锁IS 兼容 冲突 兼容 兼容
意向排他锁IX 冲突 冲突 兼容 兼容

意向锁只是标记当前有事务在操作行级锁,意向锁意向锁之间并不会冲突

手动添加意向锁

  • select * from t where id = 1 for update 添加行锁会自动添加表锁
  • lock tables t read 也可以手动添加
  • unlock tables 释放表上的排他锁

行锁实现

  1. 记录锁Record Lock:通过索引值进行加锁

  2. 间隙锁Gap LockBETWEEN 1 and 10这样的语句会产生多行记录的锁,此时就是间隙锁。代表一段区域,可以是两个索引之间、某个索引之前、某个索引之后。

  3. Next-key锁:该锁可以锁住当前位置和之前的区域,即记录锁+间隙锁。主要用于解决幻读,使用for update时通过加Next-key解决幻读。普通情况下采用MVCC解决幻读

可以使用SHOW ENGINE INNODB STATUS命令查看InnoDB监控中关于锁的事务数据
记录锁永远都是锁定索引记录,优先聚集索引,没有定义索引InnoDB会自动创建隐藏的聚集索引。


文章字数:371,阅读全文大约需要1分钟

全文索引,通过建立倒排索引,可以极大的提升索引效率,用于判断字段是否包含关键词的问题,以替代like %xx%

创建表时指定全文索引

1
2
3
4
5
6
CREATE TABLE test (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT(title, body) WITH PARSER ngram ## 全文索引,使用ngram分词模型
) ENGINE = INNODB DEFAULT CHARSET=utf8mb4 COMMENT='test表';

表中添加全文索引

1
ALTER TABLE article ADD FULLTEXT INDEX title_body_index (title,body) WITH PARSER ngram;

使用

  1. 默认模式
1
2
3
SELECT * FROM article WHERE MATCH (title,body) AGAINST ('精神' IN NATURAL LANGUAGE MODE);
## LANGUAGE 模式是默认的,所以也可以不指定
SELECT * FROM article WHERE MATCH (title,body) AGAINST ('精神');
  1. 带操作符模式
1
2
## +代表必须出现 -代表必须不出现 无符号代表可有可无,有的话排列更靠前
SELECT * FROM article WHERE MATCH (title,body) AGAINST ('+精神 -关键词 关键词2' IN BOOLEAN MODE);

自然语言的全文索引

  • LANGUAGE MODE
  • 用于匹配文章和关键字的匹配程度
  • 当一个次在超过50%的文章中都出现过,就不会被查询
  • 所以数据数需要超过4条

布尔全文索引

  • show variables like '%ft%';查看相应参数
1
2
3
4
5
6
7
8
// 默认的搜索最小单词长度和最大单词长度,中文最小应该改成1
// MyISAM
ft_min_word_len = 4;
ft_max_word_len = 84;

// InnoDB
innodb_ft_min_token_size = 3;
innodb_ft_max_token_size = 84;
  1. + 必须包含该词
  2. - 必须不包含该词
  3. > 提高该词的相关性,查询的结果靠前
  4. < 降低该词的相关性,查询的结果靠后
  5. * 星号 通配符,只能接在词后面

文章字数:222,阅读全文大约需要1分钟

NIO

  1. 同步,非阻塞IO
    NIO之所以是同步,是因为它的accept/read/write方法的内核I/O操作都会阻塞当前线程。
    但又说是非阻塞,即读取到信息的处理并不在主线程上进行,所以也是非阻塞的。
  2. 组成部分
  • Channel通道:一个可以读和写的对象,相比于只能单向的流对象,速度更快。通过IO通道可以实现异步处理。

  • Buffer`缓存:该对象本质就是一块可以写入然后读取的数据的内存块。
    使用流程

    1
    2
    3
    4
    1. 写入到缓冲区
    2. 调用buffer.flip()切换成读模式
    3. 读取数据
    4. 调用buffer.clear()或buffer.compat()方法清除缓冲区,以备下次使用。

    缓存有三个重要属性

    1
    2
    3
    1. Capacith :内存大小
    2. Position :待写入位置或者当前读取位置
    3.

文章字数:395,阅读全文大约需要1分钟

基础环境

  1. 下载nacos
  2. 配置mysql连接信息
  3. /bin/startup.cmd启动

创建普通应用

  1. 创建一个SpringBoot应用,并配置
1
2
3
4
5
6
# 表示当前是开发环境的配置文件
spring.profiles.active: dev
# 服务器端口
server.port: 8081
# 业务参数
param: 10;
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class TestController {

// 读取参数
@Value("${param:0}")
private String param;

@RequestMapping("/param")
public String getParam() {
return param;
}
}
  • 此时访问/param路径就会返回application中配置的参数

应用参数动态配置

  • 增加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
<alibaba.version>0.9.0.RELEASE</alibaba.version>
</properties>

<dependencies>
...
<!-- 增加nacos-config的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${alibaba.version}</version>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<!-- type默认是jar,pom代表引入的是父级 -->
<type>pom</type>
<!-- 只能用在dependencyManagement里,代表把springcloud的dependencyManagement内容引入,类似继承的效果。因为当前项目父级是springBoot,所以无法再指定一个springCloud的父级,所以用这种方式引入 -->
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
  • 添加配置文件bootstrap.yml
  • 这个配置文件spring会在加载application.properties之前加载,将nacos相关信息配置在这里
1
2
3
4
5
6
7
spring:
application:
name: test-service
cloud:
nacos:
config:
serer-addr: 127.0.0.1:8848
1
2
3
4
5
6
// 增加这个标签,spring在加载bean时就会自动代理相应的setter方法,从而实现动态加载配置
@RefreshScope
@RestController
public class TestController {
...
}

nacos动态配置参数

  1. 进入nacos后台http://localhost:8848/nacos/,默认用户名密码都是nacos
  2. 选择 配置管理-》配置列表
  3. 新增配置
  4. Data Id 设置为test-service-dev.properties(服务名-运行环境)
  5. 格式选择properties
  6. 增加配置内容
1
param=1
  • 重启应用即可生效

文章字数:233,阅读全文大约需要1分钟

pipLine是Netty数据链式处理的处理流,每一个节点都能处理数据并返回传入值类型的对象,再给下一个节点。一般来说会使用一些工具类对于数据现行处理,然后再进入自身的数据处理中。

字符串类型粘包和拆包

使用LineBasedFrameDecoder和StringDecoder处理之后的msg

1
2
3
4
5
6
7
8
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
arg0.pipeline().addLast(new StringDecoder());
arg0.pipeline().addLast(new NettyServerHandler());
}
}

请求中的字符串会被\n或者\r分割成一个个部分,分别被channelRead执行。一次连接中多次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 接收客户端消息,自动触发
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String mess = (String) msg;
System.out.println("服务端收到 = " + mess);
//回复消息
String respMess = message+"\n";
ByteBuf respByteBuf = Unpooled.copiedBuffer(respMess.getBytes());
ctx.write(respByteBuf);
}

http解析

1
2
3
4
//request解码
arg0.pipeline().addLast(new HttpRequestDecoder());
//request解码
arg0.pipeline().addLast(new HttpRequestDecoder());

文章字数:83,阅读全文大约需要1分钟

idea打断点出现x,并且提示错误。

没有可执行的代码,原因可能是没有编译成功,代码不可到达,等。

分析

我用的是idea,一般修改完control+f9直接热加载。可能部分代码没有加载成功,重启软件就正常了。


文章字数:154,阅读全文大约需要1分钟

这是java在运行的时候发现类未找到报出的错误,即编译成功,运行失败。

区别于编译失败

  1. ClassNotFoundException编译时类未找到
  2. NoClassDefFoundError运行时类未找到

可能的原因

  1. classpath环境或者其中的类不可用
  2. 类依赖的原生方法调用失败
  3. maven依赖传递失败
    1
    2
    1. 子模块依赖父模块的引用传递,但是却使用了<optional>true<optional/>阻断了依赖
    父模块引入依赖,子模块仅声明一下,使用依赖传递使用父类引入的依赖。