面试必背

数据库:版本 5.7.27

MySQL体系结构

image-202208091014214076吗

SQL优化:

1、尽量不要select查询 * 全部信息,只读取所需要的字段。

2、避免前缀模糊查询。

3、 避免频繁创建和删除临时表,以减少系统表资源的消耗。

4、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,一个表的索引数最好不要超过6个。

5、尽量避免在where字句中用or拼接,否则也会走全表扫描。可以通过union all 拼接代替。

SQL优化:1.定位 (druid数据源 sql监控,skywalking) 2.分析:工具 执行计划 3.方案

数据库的规范优化

SQL层面

索引分类:

​ 存储结构:b树索引、hash索引、r树索引

​ 使用: 主键索引、外键索引、唯一索引、普通索引、全文索引

MySQL中主键索引和唯一索引的区别

1.主键是一种约束,唯一索引是一种索引,两者在本质上是不同的;
2.一张表只能有一个主键,但可以创建多个唯一索引;
3.主键创建后一定包含一个唯一索引,唯一索引并一定是主键;
4.主键不能为null,唯一索引可以为null;
5.主键可以做为外键,唯一索引不行;

​ 索引面试问题:

  • 回表:二级索引查询到的索引列,如果需要查找所有列的数据,则需要到主键索引里面去取出数据。这个过程就称为回表

  • 如何避免回表:索引覆盖 将单列索引(name)升级为联合索引(name,age).

  • 索引失效:1.违背最左匹配原则 2.主键插入顺序 3.计算、函数、类型转换(自动或手动)导致索引失效;

    4.不等于(!=或者<>)导致索引失效 5.数据库和表的字符集统一使用utf8mb4

  • 如何避免索引失效:

    1.如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始,不跳过索引中间的列;

    2.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*

    3.字符串不加单引号索引失效

    4.不在索引列上做任何操作(计算,函数、自动or手动类型转换),这样会导致索引失效而转向全表扫描

    Mysql索引失效:

    1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
    注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
    3.like查询是以%开头
    4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
    5.索引本身失效

    索引优化

    1、能使用limit的时候尽量使用limit

    2、设计索引的时候,尽量保证主键足够小。

    3、尽量保证数据的有序性,提升批量插入的性能。

    4、查询条件like以通配符%开头,如 name like (“%abc”),索引失效,导致全表扫描

索引的机制和底层原理

索引的数据结构和具体的存储引擎实现有关,mysql中使用较多的索引有Hsah索引,B+树索引等(索引就是数组,数组就是索引),哈希索引——底层数据结构是哈希表,B+tree索引——底层数据结构是B+树。

B+树

B+树索引的构造类似于二叉树,根据键值快速找到数据。B+树是由B树演化而来的。

B+树是为磁盘或者其他直接存取辅助设备设计的一种平衡查找树。在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接。
B树每个节点都存储数据,所有节点组成这棵树。B+树只有叶子节点存储数据(B+数中有两个头指针:一个指向根节点,另一个指向关键字最小的叶节点),叶子节点包含了这棵树的所有数据,所有的叶子结点使用链表相连,便于区间查找和遍历,所有非叶节点起到索引作用。

B树中叶节点包含的关键字和其他节点包含的关键字是不重复的,B+树的索引项只包含对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。

mysql的事务

​ 默认的事务:一条sql语句就是一个事务 默认就开启事务并提交事务

手动事务:1)显示的开启一个事务:start transaction2)事务提交:commit代表从开启事务到事务提交 中间的所有的sql都认为有效 真正的更新数据库3)事务的回滚:rollback 代表事务的回滚 从开启事务到事务回滚 中间的所有的 sql操作都认为无效数据库没有被更新

事务的四大特征:

  • 原子性:对同一事物操作,要么同时成功,要么同时失败,失败对数据库不能有影响
  • 一致性:事务操作后,数据库内的数据总量保持一致。
  • 隔离性:相同的表,不同事物之间不能互相干扰
  • 持久性:事务一旦提交,数据就会进行永久化保存

事务的隔离级别:

  • 未提交读:可能存在【脏读、不可重复读、幻读】的问题
  • 以提交读:可能存在【不可重复读、幻读】问题
  • 可重复读:可能存在【幻读问题】
  • 串行读:效率低

什么是脏读?幻读?不可重复读?

脏读(Drity Read):是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致。

不可重复读(Non-repeatable read)【MySQL默认】:事务开启后关闭前,多次读取同一条记录,结果却不能保证一致,所以叫不可重复读。主要问题不在同一个数据库的问题,而在不同的服务器,不同数据库时会出现的问题,因为两台电脑之间要保证数据相同,是需要时间进行复制的,从表在复制主表的过程中,很可能因为修改数据过快而导致复制到错误数据。

幻读(Phantom Read):select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在执行delete删除,却发现删除成功。

事务的隔离级别是怎么解决以上三种问题的?

解决脏读:修改时加排他锁(写锁),直到事务提交后才释放,读取时加共享锁(读锁),其他事务只能读取,不能再有更新操作。防止脏读。

解决不可重复读:innodb引擎采用了MVCC(多版本并发控制)来解决不可重复读问题。mvcc是利用在每条数据后面加了隐藏的两列(创建版本号和删除版本号)当执行查询的时, 当前查询版本号>= 创建版本号 并且 >删除版本号 , MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销。

解决幻读:采用next-key锁解决幻读问题,next-key锁包含两部分:记录锁(行锁)+间隙锁,就是在索引和索引之间上面加锁。

分库分表

  • 分库:将一个库的数据拆到多个相同的库中,访问的时候访问一个库
  • 分表:把一个表的数据放到多个表中,操作对应某个表就行
  • 实现:垂直分库(表)和水平分库(表)

redo log和undo log的区别

innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作(binlog中有数据,commit),undo log是回滚日志,提供回滚操作。

持久性:Innodb的redo log 记录数据修改的操作日志,当事务提交的时候,会将数据刷盘.

原子性:Innodb的undo log 记录旧数据,进行事务回滚

数据库宕机重启后,会将redolog数据恢复到数据库中,再根据undo log和binlog内容决定是回滚事务还是提交事务.

union和union all的区别

一、区别1:取结果的交集

1、union: 对两个结果集进行并集操作, 不包括重复行,相当于distinct, 同时进行默认规则的排序;

2、union all: 对两个结果集进行并集操作, 包括重复行, 即所有的结果全部显示, 不管是不是重复;

二、区别2:获取结果后的操作

1、union: 会对获取的结果进行排序操作

2、union all: 不会对获取的结果进行排序操作

MQ

RabbitMQ:

  • 基于AMQP协议的消息中间件
  • 一个提供统一消息服务的应用层标准高级消息队列的链接协议

RabbitMQ五种工作模式:

1、简单模式:就是不通过交换机,消息直接通过队列,一对一收发。

2、工作队列模式:也是不通过交换机,消息直接通过队列,只是一个发送方可以有多个接收端。

3、发布订阅模式:由交换机分发消息到不同队列,每个消费者只监听自己的队列。

4、路由模式:由交换机分发消息,但是发送方需要指定路由key,交换机会根据不同的routing key分发给不同的队列,消费方对应自己需要的队列。

5、通配符模式:和路由模式有些相近,只是通配符模式可以在绑定routing key时使用通配符。

6、RPC模式:RPC远程调用模式,严格来说不太算是MQ。

避免消息堆积

  • 优化消费者代码,提高消费者能力
  • 给消费者设置生命周期,超时就丢弃掉
  • 设置队列的最大长度

MQ如何保证顺序消费

将原来的一个queue拆分成多个queue,确保每个queue都有一个自己的consumer

如何防止消息丢失呢?

1、在生产者丢失——confirm确认模式

使用RabbitMQ事务机制,但它是同步的,且很耗性能。

开启confirm确认模式,确认消息是否从“生产者”发送到“交换机”,成功回传ack消息,失败可以重试或抛异常。且confirm模式是异步回调接口通知MQ是否接收到消息。一般都采用这种方式。

2、在MQ中丢失——持久化

开启RabbitMQ持久化,防止RabbitMQ自己弄丢数据。除非极小概率还没来得及持久化,MQ就先挂了,即使这样,也只会丢失极少的数据量。所以,持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 ack,你也是可以自己重发的。但持久化的过程也是很耗性能的。

3、在消费者丢失——ack机制

用 RabbitMQ 提供的 ack 机制,简单来说,就是你必须关闭 RabbitMQ 的自动 ack,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里 ack 一把。这样的话,如果你还没处理完,不就没有 ack 了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的

RabbitMq如何保证消息的可靠性:

由生产者发送消息给MQ,MQ会将消息给到消费者,消息者收到消息后,会返回一个消息确认,这个“消息确认”会被MQ放到“回调检查服务”中,“回调检查服务”会将收到的消息给到定时检查的MDB。

但在这个过程中,如果出现异常或者网络波动,就会导致消息到不了回调检查服务,所以为了保证能够消息可靠性,会由生产者延迟一段时间后,再发送一个相同的消息给MQ,这个消息会直接被MQ发送到回调检查服务。

回调检查服务会将延迟发送的消息和MDB中的消息对比,如果MDB中没有该消息,就会调用生产者,让生产者重新发送消息。

但在以上过程,还可能出现“延迟发送消息”也出问题,为了更深层保证消息的可靠性,还需要一个定时检查服务,每隔一段固定时间,定时检查服务会将MDB里的消息和DB中的消息进行匹配(检查某个时间段的表,而不是全表扫描),如果有MDB缺失的消息,就会调用生产者重新发送消息。

如何保证幂等性

可以在生产消息的时候给每个消息加一个全局唯一ID,消费者消费消息时根据这个ID去redis当中查询之前是否消费过。如果没有消费过,就进行消费并将这个消息的ID写入到redis当中。如果已经消费过了,就无需再次消费了。

方法一:采用乐观锁机制保证消息幂等性。在数据库中会增加一个版本字段,执行时也会匹配版本,如果版本不一致,SQL语句的匹配就不成立,就不会执行。

方法二:你拿到这个消息做数据库的insert操作,那就容易了,给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

方法三:你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。

equals和==的区别

==比较的是变量(栈)内存中存放的对象的(堆)内存地址,来判断两个对象的地址是否相同

equals用来比较的是两个对象的内容是否相等

Spring

常用注解

@Component 、@Controller 、@Service 、@Repository、@Bean、@Aspect、@Before、

@Autowired 通过什么自动装配 :类型

自动配置 起步依赖

IOC:控制反转

把new对象的工作交给spring完成

  • 原理:工厂模式+反射机制。

AOP:动态代理

面向切面编程,切面,切点以及通知

可用于权限认证、日志、事务处理等

通知类型:

  • 前置通知

  • 后置通知

  • 返回通知

  • 异常通知

  • 环绕通知

    spring声明式事务

创建Bean对象的方式

1、通过IoC直接创建bean对象

2、通过IoC创建bean工厂,再通过bean工厂的方法创建bean对象

3、通过IoC创建bean工厂,再通过bean工厂的静态方法创建bean对象

依赖注入

1.**Set方法注入:**注入最简单,最常用的注入方式,支持注解+xml。

2.构造器注入:是指带有参数的构造函数注入,支持注解+xml

3.**静态工厂的方式注入:**通过调用静态工厂的方法来获取自己需要的对象,只支持xml。

4.实例工厂的方式注入:获取对象实例的方法不是静态的,所以需要new一个工厂类,再调用普通的实例方法,只支持xml。

Bean的生命周期:

1、实例化:基于类,根据类的无参构造,通过反射得到一对象

2、填充属性:spring容器会将对象里的属性进行赋值

3、初始化:实现容器中的一系列接口,进行初始化

4、销毁:调用destroy()接口方法或者调用自定义的销毁方法

spring事务的传播行为

  • REQUIRED(required):Spring的默认传播级别,如果存在事务则加入当前事务,如果不存在事务则新建事务执行。
  • SUPPORTS(supports):如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。
  • MANDATORY(mandatory):这个传播级别要求上下文中必须存在事务,不然就会抛出异常。
  • REQUIRES_NEW(requires_new):该传播级别每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。
  • NOT_SUPPORTED(not_supported):当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复事务。
  • NEVER(never):该传播级别要求上下文中不能存在事务,否则抛出异常。
  • NESTED(nested):嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务。

springcioud

fegin的调用

Feign是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样,从避免了 调用目标微服务时,需要不断的解析/封装json 数据的繁琐。

1 在服务中加入Fegin的依赖 2 、在主类上添加Fegin的注解:@EnableFeignClients 3、服务中创建一个service, 并使用Fegin实现微服务调用 4 注入service

springboot

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

@ComponentScan:Spring组件扫描。

springboot的自动装配

SpringBoot的自动装配也就是通过@EnableAutoConfiguration注解,加载AutoConfigurationImportSelector类中的selectImports方法,进而扫描spring.factories文件下的自动配置类,并将其装配到IOC容器的过程

volatile关键词:

1.保证可见性:这个关键字主要是用在多线程的时候。就是一个线程修改了变量的值,那么其他的线程能立刻看到修改之后的值

2.解决指令重排序问题

@autowired通过什么自动装配

byType,如果要通过byname用@Qualifier(属于spring规范)

第二种用@Resource(属于J2EE规范)

Dubbo

Dubbo架构是什么样的?

1、服务提供方

2、服务消费方

3、注册中心

4、监控中心

Dubbo怎么调用的

使用@reference注解调用,2、指定dubbo服务端口进行调用 写入我们的这个ip地址和端口

dubbo支持那些协议:

  • dubbo(默认)
  • RMI
  • WebService
  • HTTP
  • Hession
  • Redis
  • Memcache

*Dubbo 使用过程中都遇到了些什么问题?*

1、在注册中心找不到对应的服务,检查 service 实现类是否添加了@service 注解无法连接到注册中心,检查配置文件中的对应的测试 ip 是否正确

2、Dubbo传递返回对象时报了no serilizable(无序化)的错误。因为我们用的是MyBatis-Plus,每次用Dubbo进行queryWrapper对象传输时都会报错,后来发现,是因为MyBatis-Plus里面的queryWrapper对象是没有做序列化的,底层没有实现seriliazable接口,无法通过Dubbo进行传递,所以最后我将对象里的数据再拆出来再返回数据。

(需要补充的是,MyBatis-Plus目的是为了强化MyBatis-Plus并简化开发,它主要支持了Lambda,并没有进行对象序接化处理,而Dubbo的远程调用对象是必须要将对象序化化为二进制传输的,所以这里无法直接用Dubbo传递queryWrapper对象。)

3、遇到隐式传参丢失问题:通过RpcContext上下文对象进行隐式传参,但RpcContext进行参数传递时是存在生命周期,只在一次调用中生效,当连续调用多次,参数就会丢失。类似request生命周期。因为它底层的ThreadLocal要保证线程安全,所以每次调完后,dubbo里面会将请求里的信息清除。

Nacos

nacos原理是什么?

nacos本质上就是注册中心,服务地址注册和拉取;

Nacos是微服务架构中的注册中心和配置中心,其他服务的服务信息(ip,端口等信息)可以注册到nacos服务端。nacos又为客户端提供了服务发现的功能。客户端会开启一个定时任务,定时向服务端获取最新的服务列表,加载到客户端本地缓存。客户端同时又开启一个定时心跳发送的任务,用于告知服务端,当前服务的健康状态。服务端启动的时候同样也会开启一个健康检查的定时任务,扫描服务列表,将长时间未与服务端发送心跳的服务的健康状态改为false,达到某个时间,会踢出该服务。
Nacos好处就是服务不需要记录其他服务的ip信息,通过nacos可以实时获取其他服务列表。仅仅只需从本地缓存中根据服务名找到服务列表,利用负载均衡算法从列表中拉取一个ip进行调用(比如Ribbon)。

Mybatis

执行流程

1、 创建SqlSessionFactory

2、 通过SqlSessionFactory创建SqlSession

3、 通过sqlsession执行数据库操作

4、 调用session.commit()提交事务

5、 调用session.close()关闭会话

一级缓存与二级缓存

一级缓存 作用域在 seassion 中 关闭 seassion连接或刷新就消失, 这是默认
二级缓存 可以使用中间件进行存储起来

Redis

Redis过期时间怎么设置?

expire key timestamp

在set key时,可以给一个expire time,就是过期时间,通过过期时间可以指定key可以存货的时间

如果设置了一批key只能存活1个小时,那么接下来1小时候,redis是怎么对这批key进行删除?

定期删除+惰性删除

1、定期删除:redis是默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。

这里是随机抽取的,为什么要随机抽取呢?如果redis中存了几十万的key,每隔100ms就遍历所有的设置过期时间的可以,会给cpu带来很大的负担

2、惰性删除:定期删除可能会导致很多过期key到了时间并没有被删除。所以就有了惰性删除。

假如过期的key,靠定期删除没有被删除调,还停留在内存里,除非你的系统去查找对应的key,才会被redis删除。这就是惰性删除

如果仅仅设置过期时间还是有问题的。如果定期删除漏掉了很多过期key,然后你也没法及时去查,也就没走惰性删除,此时就会有大量的过期的key堆积在内存理,会导致redis内存块耗尽

常见数据类型

  • String、List、Set、Hash、Zset

使用Redis有序集合ZSET来实现Top10:

1、保存搜索词,将搜索词保存到redis的同时设置一个分数值,如果是同一个搜索词分值加一,另外考虑到热搜版都是当日的,所以我们需要设置一个统一的过期时间。

2、获取搜索词,根据分支来获取前5位。

说说Redis的持久化方式有哪些?

有两种:RDB和AOF。

RDB是二进制快照形式,数据体量小,保存效率高,但丢失风险也较大,因为它是定时定量更改才会自动持久化,无法实时存储,如果在快照之前丢失,则无法找回。

AOF是过程命令形式,数据体量大(可以用AOF重写解决该问题),效率低于RDB,记录每个操作,存储格式也更复杂,但数据相对完整(最快可以每秒同步一次),且弥补了RDB不能实时存储的缺点

redis key删除策略

  • 主动删除 :redis定时扫描设置了过期时间的key集合,每秒做10次。
    • 1 随机从这个设置了过期key的集合中 取20 个key
    • 2 删除所有已经过期了的key
    • 3 如果所有过期的key超过 25% ,在做一次(1)
  • 被动删除:比如有一个key过期了,不会立即删除,当有人访问这个key的时候,会查看这个key过期时间,如果过期直接删除,返回 nil。

reids哨兵

第一个就是作用是 监控
Sentinel 会不断检查您的master和slave是否按预期工作 判断他们的是否健康

第二个作用呢是 自动故障的恢复
如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也还是以新的master为主

第三个作用呢就是 通知
Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

缓存雪崩:

概念:在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩

解决:加锁排队可以起到缓冲的作用,防止大量的请求同时操作数据库

缓存穿透:

概念:缓存穿透是指查询数据库和缓存都无数据,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。

解决:使用布隆过滤器来减少对数据库的请求,过滤掉一定不存在的无效请求

缓存击穿:

缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿。

解决:1、加锁排队,不让大量请求同时操作数据库

​ 2、将过期时间设置永不过期,只要在后续及时更新热点即可。

说一说Redis分布式锁

Redis分布式锁的加锁,其实就是给Key键设置一个值,其他进程执行前会判断Redis中这个Key是否有值,如果发现这个Key有值了,就说明已有其他进程在执行,则循环等待,超时则获取失败。解锁就是将Key键删除,为了保证解锁的原子性操作,用Redis自带的LUA脚本完成操作。

Redis分布式锁应该注意什么?

①加锁的时候,应考虑到执行一半宕机或故障导致没能执行到解锁的命令,产生死锁,所以需要给定一个过期时间,防止死锁。

②解锁要保证原子一致性。

③redis的解锁就是把key删除即可,但是删除的时候不能随便删,比如线程A不能删除线程B的key,这个时候value就起到作用了,random_value我们设置为随机值,每一个线程都生成一个随机值作为random_value,删除key的时候先判断随机值是否和本线程的一致,一致的才可以删除。

线程池

线程池用来解决什么问题?

用来解决业务中消息堆积问题。

线程的6种状态是什么?

1、新建状态(new):创建线程对象。

2、就绪状态(runnable):start方法。

3、阻塞状态(blocked):无法获得锁对象(线程没抢到)。

4、等待状态(waiting):wait方法。

5、计时状态(timed_waiting):sleep方法。

6、死亡状态(terminated):全部代码运行完毕。

sleep() 和 wait() 有什么区别?

其中wait()方法和sleep()方法都能够暂停线程的执行,他们的不同之处在于:

sleep()方法本质是占用线程,如果获得了锁,其他线程进不来。

wait()方法会释放锁资源,其他线程可以进来。

wait()方法被调用后,线程不会自动唤醒,需要调用notify()或者notifyAll()方法。

sleep()方法执行完成后,线程会自动苏醒。

线程池Executors类有哪几种常见的线程池?

4种:单例线程池、固定大小线程池、可缓存线程池、大小无限线程池。

(1)单例线程池: 创建一个单例线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

(2)固定大小线程池:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。如果希望在服务器上使用线程池,建议使用 newFixedThreadPool方法来创建线程池,这样能获得更好的性能。

(3)可缓存线程池:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。

(4)大小无限线程池:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

ThreaPoolExecutor 自定义线程池

可以自定义创建线程池,具体参数可以走它的构造函数。

线程池七大的核心参数有哪些?

七个核心参数:

参数一:核心线程数(不能小于0)

参数二:最大线程数(>=核心线程数)

参数三:临时线程最大存活时间(不能小于0)

参数四:时间单位(参数三的单位)

参数五:等待列队(不能为null)

参数六:创建线程工厂(不能为null,一般用默认线程工厂)

参数七:任务的拒绝策略(不能为null)

线程池的拒绝策略有哪些?

4种:

1、ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出异常(默认);

2、ThreadPoolExecutor.DiscardPolicy:丢弃任务,不抛异常(不推荐);

3、ThreadPoolExecutor.DiscardOldestPolicy:丢弃等待最久的线程;

4、ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程(main)运行run方法。

**当任务过多时,

线程池的执行顺序是怎么样的?

1、核心线程满后;

2、阻塞队列满后;

3、临时线程满后(最大线程数 - 核心线程数 = 临时线程数);

4、拒绝策略。

线程池中 submit() 和 execute() 方法有什么区别?

接收参数:execute()只能执行 Runnable 类型的任务。submit()可以执行 Runnable 和 Callable 类型的任务。
返回值:submit()方法可以返回持有计算结果的 Future 对象,而execute()没有。
异常处理:submit()方便 Exception 处理。

多线程

线程安全问题和解决方法

一、导致线程不安全的原因

1.线程争抢,抢占式执行。

2.多个线程同时修改了同一个变量。

3.操作非原子性操作。

4.内存可见性问题。

5.指令重排序。

二、解决线程安全问题的方法

1.volatile解决指令重排序问题和内存可见性问题。

2.用锁,synchronized 锁或者lock锁

创建线程有哪几种方式?

创建线程有四种方式:
1、继承 Thread 类;
2、实现 Runnable 接口;
3、实现 Callable 接口,创建 FutureTask (未来任务)对象(FutureTask 也是Runnable 接口的实现类),与Runnable的区别是有返回值;
4、使用创建线程池

string、stringbuffer、stringBuilder

String类是不可变类,当一个String对象被创建,则包含在对象中的字符序列是不可改变的,直至对象被销毁;StringBuffer对象代表可变字符串对象,且线程安全;StringBuilder类代表可变字符串对象,且非线程安全。

为什么stringbuffer线程安全

主要因为StringBuffer很多方法都是synchronized 修饰的,而线程安全的优势就是他可以在多线程环境下使用。多线程不要用StringBuilder,否则会出现问题。

线程并发

当多个线程在操作的时候,如果系统只有一个CPU,同一时刻就只有一条线程指令在执行,但是各个线程指令被快速的轮换执行,这就是的宏观上看起来是多个线程在同时执行。但微观上并不是这样,只是将时间分成若干个片段,多个线程交替执行。

系统只有1个CPU,线程就要通过竞争得到执行机会。谁得到CPU谁就执行。

说一下 runnable 和 callable 有什么区别?

相同点:
1、都是接口
2、都可以编写多线程程序
3、都采用Thread.start()启动线程
主要区别:
1、Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
2、Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息。
注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

LINUX常用命令

ls 查看当前目录下的内容

pwd 查看当前所在目录

cd [目录名] 切换目录

touch [文件名] 如果文件不存在,创建文件

mkdir [目录名] 创建目录

rm [文件名] 删除指定文件

JVM

定义

  • 每个线程运行时所需要的内存, 称为虚拟机栈
  • 每个栈由多个栈帧(包含参数, 局部变量, 放回值)组成, 对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧, 对应着当前正在执行的那个方法

定义:

  • 通过new关键字, 创建对象都会使用堆内存
    特点:
  • 它是线程共享的, 堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制(不在被引用的对象会被回收
    堆内存溢出:
  • 不断产生对象, 并且这些对象不断被使用(不能被作为垃圾被回收), 一旦堆内存内耗尽也就是溢出

方法区

  • 所有java虚拟机线程的共享的区, 它存储跟类相关的信息, 类的成员变量, 方法数据, 成员方法及构造器方法的代码部分(特色方法, 类的构造器), 运行时常量池
  • 在虚拟器启动时创建, 逻辑上是堆的组成部分(概念), 不同厂商有不同实现(不强制), jdk1.8之前实现方法是永久代(堆内存的一部分), jdk1.8之后用元空间实现使用本地内存(操作系统)
  • 也会导致内存溢出

我负责的主要是门店管理和收银端服务

CAP 定理:

Consistency (一致性)

Availability (可用性):

Partition Tolerance (分区容错性):

BASE理论

BASE:

BA 基本可用 允许必要时牺牲部分可用,保障一致性

S 软状态 允许存在不影响整体的中间状态

E 最终一致性

Basically Available(基本可用,必要时可牺牲部分可用,成全一致性),Soft state(软状

态,充许存在中间状态但不影响整体),和 Eventually consistent(最终一致性)。

登录流程实现:

在项目中有一个专门用作认证的微服务(认证服务),在对其他微服务进行操作时,用户一定的先登 录,所以登录时就会涉及到这么一个流程。用户输入账号密码,通过网关进行转发到认证服务中,再根 据数据库查询到的账号密码进行比较。特别要注意的是这边我们将密码进行了加密处理,数据库保存的 是加密后的密码(MD5),校验通过后会生成一个令牌(token),会将令牌进行返回,令牌写在(HTTP 的:Authorization)中,尽量不提及。在后续对其他微服务进行访问时,将会携带令牌,并将令牌将会 在网关出进行解析并判断。正确后才可以访问其他微服务。

JWT 跟网关做鉴权

而JWT就是上述流程当中token的一种具体实现方式,其全称是JSON Web Token,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得 到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各 方之间安全地将信息作为Json对象传输。JWT的认证流程如下:

首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建 议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进 行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退 出登录时删除保存的JWT Token即可 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题) 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方 是否是自己等等 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权 限等),返回结果

数据同步:

在订单微服务中,当我们对菜品搜索功能进行测试时,我们发现了一个问题,当菜品表中,菜品信息记录到一定数量时,这时候前台搜索菜品的效率就非常低,后面我们发现原因就是MySql在使用模糊查询时,不会走索引,而会进行全表扫描,导致了搜索性能极低。

所以,我们就引进了当下比较主流的搜索引擎ElasticSearch和MySql搭配使用。就我了解到ElasticSearch之所以可以进行高速搜索,主要就是他可以通过倒排索引,将文档进行分词,也就是将一条内容拆成多个词条,并于内容一一对应,到达快速搜索的实现。

但是在后续的测试中,我们又发现了一个问题就是当我们在数据库中对菜品表进行增删改时,ElasticSearch并不能读取到我们新增的数据,为了解决这一数据同步问题,我们开会讨论提出了三种数据同步方案。

1、使用Canal+MQ

canal主要是根据mysql的主从复制原理,将自己伪装成一个MySql的从机,向mysql 主机发送dump协议,并且通过解析接受到的binary log对象来实现数据同步。

2、使用Logstash

logstash的基本流程架构:input | filter | output 如需对数据进行额外处理,filter可省略(条件)。

Logstash可以实现新增同步和全量同步,我们使用的是新增同步,在MySQL表上需要加上时间字段,利用logstasg的input每天定时扫出当前有改动或新增的数据,并通过output同步到ES中,但是存在一个缺点,就是当我们对数据进行删除时,ElasticSearch不会扫描到,所以我们采用了逻辑删除的方式来确保数据的安全性。

使用RabbitMQ实现消息下发

在我们的用户微服务中,有一个短信下发功能,就是当商户进行入驻时,后台会为商户注册一个登录账号,但是我们需要将生成的密码下发给用户。这时我们首先考虑到的是如何直接调用第三方接口来进行短信下发,产生大量的代码耦合,这在微服务开发中,是不被允许的。因此我们就首先采用了MQ消息中间来进行消息传递。首先我们考虑到别的微服务也要用到MQ,所有我们就单独把它作为一个微服务,然后定义了统一消息格式,然后为了方便使用我们采用了AOP来进行开发,定义一个@SenMessage注解,根据注解进行切入,调用第三方接口完成短信业务的发送。

多线程实现短信下发业务:

考虑到经常会给大批量用户同时发送短信或者消息,为了提高效率,我们封装了一个计算线程的工具类,根据工具类得出需要创建多少个线程,利用多线程进行短信下发业务的实现。

分布式事务实现

​ TCC主要的应用场景为跨服务间的调动, 当用户下单时,需要考虑扣除沽清。但是此时的操作需要经过订单服务和门店服务,所以需要考虑分布式事务的实现,避免出现数据的不一致。我们这个项目中所用的技术就是阿里巴巴的Seata,那么 seata的实现原理是首先TC作为我们的事务协调器在这里是有Seata充当,TM作为事务管理器也就是事 务发起方在我们的这个业务中是由controller中的下单方法充当,RM资源管理器作为的参与方在这个业 务中由订单服务以及店铺服务充当,执行流程为TM会在TC中创建一个全局事务TC会向TM返回一个全局 事务XID,TM调用RM并将XID传递给RM,RM会根据XID在TC中创建事务分支,若每个分支事务都执 行成功那么全局事务成功提交,若有一方RM执行失败则整个全局事务进行回滚以上就是我对seata的一 些了解

订单数据迁移

为什么迁移:MySQL单表数据超过量级,性能会下降

ElasticJob是面向互联网生态和海量任务的分布式调度解决方案,由两个相互独立项目Elasticjob-Lite和

ElasticJob-cloud组成的。

补充:

JAVA基础 创建对象方法:

new 一个对象

Object.clone()一个对象

反射创建一个对象

如何解决线程安全问题(资源竞争):

jdk1.8新特性Stream流map方法作用:

Volatile关键字作用:

Java 提供了 volatile 关键字来保证可见性禁止指令重排(一定有序)。 volatile 提供 happensbefore 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会 保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

深拷贝和浅拷贝:

浅拷贝就是 拷贝以后两个指针指向同一个内存地址

深拷贝是拷贝了所有的内容拷贝到另一个内存地

设计模式

策略模式 单例模式

redis设置过期时间

使用expire

常见的异常

NullPointerException: 空指针异常。经验发现这个异常是经常会发生的,属于运行时异常。

ClassCastException: 类型强制转换异常。Java的一些语法知识不太熟悉的时候会发生的异常

ArrayIndexOutOfBoundsException: 数组下标越界异常。特别常见的一个异常,粗心的程序员都会犯的错误。

ArithmeticException:算术运算异常。

HTTP协议

1、http协议的请求方式是什么?

答:HTTP是一个基于TCP/IP通信协议来传递数据,包括html文件、图像、结果等,即是一个客户端和服务器端请求和应答的标准。基本上用到的就是GET和POST,充其量再遇到个option请求。

2、http和https有什么区别?

答:(1)https有ca证书,http一般没有;(2)http是超文本传输协议,信息是明文传输。https则是具有安全性的ssl加密传输协议;(3)http默认80端口,https默认443端口。

3、cookies机制和session机制的区别是什么?

答:(1)cookies数据保存在客户端,session数据保存在服务端;(2)cookies可以减轻服务器压力,但是不安全,容易进行cookies欺骗;(3)session安全一点,但是占用服务器资源。

Cookie机制基本介绍

cookie机制采用的是在客户端保持 HTTP 状态信息的方案。当浏览器访问WEB服务器的某个资源时,WEB服务器会在HTTP响应头中添加一个键值对传送给浏览器,再由浏览器将该cookie放到客户端磁盘的一个文件中,该文件可理解为cookie域(键值对的集合),往后每次访问某个网站时,都会在请求头中带着这个网站的所有cookie值。

Session机制

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

RPC

  1. 解决分布式系统中,服务之间的调用问题。

  2. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。

RPC能解耦服务

RPC:远程过程调用。RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。

通过RPC能解耦服务,这才是使用RPC的真正目的。RPC的原理主要用到了动态代理模式,至于http协议,只是传输协议而已。简单的实现可以参考spring remoting,复杂的实现可以参考dubbo。

RPC框架好处

http接口是在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;

优点就是简单、直接、开发方便。

如果是一个大型的网站,内部子系统较多、接口非常多的情况下,RPC框架的好处就显示出来了:

首先就是长链接,不必每次通信都要像http一样去3次握手什么的,减少了网络开销;

其次就是RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。

最后是安全性。

redis缓存实现session单点登录

把session数据存放在redis,统一管理,向外提供服务接口,redis可以设置过期时间,对应session的失效时间

优点:存取速度快,效率高;无单点故障,可以部署集群;自定义登录页面(即每个应用都可以设计自己的登录页面)

缺点:必须部署redis;所有程序自行开发,例如:登录、登出等

  • 我们用户模块连接同一个redis实例,比如6379端口的
  • 在登陆时我们根据用户名和密码的登陆逻辑判断用户是否登陆成功
  • 如果登陆成功,将sessionid或者是一个UUID只要唯一就可以了当做key,将用户当做value存到redis中,并将该key设置过期时间为30分钟过期
  • 之后我们的后台再生成一个cookie,将这个sessionID当做cookie的值,cookie的键可以自己取名字,将这个cookie种到浏览器上,
  • 用户再来访问网站时就会携带cookie,我们遍历cookie找到我们需要的那个cookie,将value取出,根据value也就是上一次存储的sessionid,到Redis中查用户,如果能查到说明登陆过,查不到就没有登陆过,或者用户登录超时了
  • 用户退出登录时,将cookie删除,将redis中数据删除
  • 如果用户做了其它任何操作,我们再写一个过滤器,重置key的有效期,否则用户登陆进来就只能玩30分钟,太短了是不!

Arraylist去重、排序

去重:那么可以先考虑把ArrayList转化为一个临时的HashSet,再把这个临时的HashSet转化回ArrayList。因为HashSet里面的元素是不可重复的嘛!

排序:使用Collections.sort()传入ArrayList,会采用默认的方式进行排序使用Collections.sort()传入ArrayList和自己实现Commparator接口的类的对象,实现自定义排序。

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

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

相关文章

QT:QT与操作系统

文章目录 信号槽与事件QT多线程概述原理完成倒计时程序 UDP回显服务器服务端客户端 信号槽与事件 在之前的信号槽中&#xff0c;已经有了一个基本的认识&#xff0c;那么对于QT中事件的理解其实就非常的类似&#xff0c;当用户进行某种操作的时候&#xff0c;就会触发事件&…

CCF-Csp算法能力认证,202206-1归一化处理(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

欧洲杯/奥运会-云直播

欧洲杯/奥运会要来了&#xff0c;如何升级自己的网站让你的顾客都能观赏直播已提高用户量呢&#xff1f;&#xff01; 【功能完善、平滑兼容】 云直播支持 RTMP 推流、 HLS 源站等多种直播源接入方式&#xff0c;提供直播 SDK&#xff0c;支持多终端适配&#xff0c;上行码率…

Unity之ShaderGraph入门简介与配置

前言 ShaderGraph是Unity的一个可视化着色器编辑工具,它允许开发者在不编写代码的情况下创建复杂的着色器效果。ShaderGraph提供了一个直观的图形界面,用户可以通过拖拽节点并连接它们来构建自定义的着色器。用户可以在ShaderGraph中使用各种节点,如数学运算、纹理采样、颜…

报表-接口类型的数据源

1、配置 在数据中进行如下配置 配置格式&#xff0c;换行的方式 #API $.data[0].children http://192.168.1.1:9200/apis/getInfo 行1&#xff1a;固定写法&#xff0c;标识这是一个接口类型的数据集 行2&#xff1a;JSONPath格式字符串&#xff0c;对接口的数据进行取值。…

Linux Ubuntu(玩客云) qBittorrent docker BT下载(qbittorrent 密码错误无法登录 ip地址被禁止登录等)

提示&#xff1a; 需要提前安装Docker 根据qBittorrent官网的更新日志https://www.qbittorrent.org/news &#xff0c;4.6.1.0包含一个重大更新。可以看到自4.6.1.0开始&#xff0c;qBittorrent将弃用adminadmin默认密码&#xff0c;采用随机密码&#xff0c;将在终端控制台输出…

OpenSearch 与 Elasticsearch:7 个主要差异及如何选择

OpenSearch 与 Elasticsearch&#xff1a;7 个主要差异及如何选择 1. 什么是 Elasticsearch&#xff1f; Elasticsearch 是一个基于 Apache Lucene 构建的开源、RESTful、分布式搜索和分析引擎。它旨在处理大量数据&#xff0c;使其成为日志和事件数据管理的流行选择。 Elasti…

#友元函数与友元类

目录 1.概念 2.友元函数 3.友元类 1.概念 友元提供了一种突破封装的方式&#xff0c;有时提供了便利。但是友元会增加耦合度&#xff0c;破坏了封装&#xff0c;所以友元不宜多 用。 友元分为&#xff1a;友元函数和友元类 2.友元函数 友元函数可以直接访问类的私有成员&a…

计算机网络学习记录 物理层 Day2

计算机网络学习记录 你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner gitee https://gitee.com/Qiuner 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会…

【八股系列】vue的双向绑定原理是什么?

Vue 的双向绑定是通过数据劫持和发布-订阅模式实现的。 在Vue中&#xff0c;每一个组件都有一个对应的Watcher实例&#xff0c;它会观察组件中所有需要被双向绑定的属性。当属性的值发生变化时&#xff0c;Watcher会通知相应的Dep对象&#xff0c;Dep对象会通知所有订阅它的Wat…

Android build.prop生成过程源码分析

Android的build.prop文件是在Android编译时刻收集的各种property【LCD density/语言/编译时间, etc.】&#xff1b;编译完成之后&#xff0c;文件生成在out/target/product/<board【OK1000】>/system/目录下&#xff1b;在Android运行时刻可以通过property_get()[c/c域] …

C++ 字符串补充(二)

1、字符串输入操作 在C中&#xff0c;可以使用std::getline(std::cin,变量名)函数来实现包含空格的字符串输入。 代码示例&#xff1a; #include <iostream> using namespace std;int main() {string str;cout<<"请输入包含空格的字符串:";getline(ci…

BI赋能金融新质生产力,16家金融机构智能BI创新实践分享

2024年政府工作报告强调&#xff0c;要“大力发展科技金融、绿色金融、普惠金融、养老金融、数字金融”&#xff0c;同时“大力推进现代化产业体系建设&#xff0c;加快发展新质生产力”。对于金融行业而言&#xff0c;培育新质生产力是高质量发展的关键着力点。金融机构可以通…

Linux学习笔记3---WSL2交叉编译

ARM 裸机、Uboot 移植、Linux 移植这些都需要在 Ubuntu 下进行编译&#xff0c;编译就需要编译器&#xff0c;在上一章里面已经讲解了如何在 Liux 进行 C 语言开发&#xff0c;里面使用 GCC 编译器进行代码编译&#xff0c;但使用的 gcc 编译器是针对 X86 架构的&#xff01;而…

极简—springMVC工作流程

1、流程图 2、流程 发起请求&#xff1a;客户端通过 HTTP 协议向服务器发起请求。前端控制器&#xff1a;这个请求会先到前端控制器 DispatcherServlet&#xff0c;它是整个流程的入口点&#xff0c;负责接收请求并将其分发给相应的处理器。处理器映射&#xff1a;DispatcherS…

自动控制原理学习--平衡小车的控制算法(二)

上一节 在matlab建模&#xff0c;这一节PID控制. 一、模型 直接先放一张matlab simulink的模型&#xff08;只有直线速度环和平衡环&#xff0c;串联PID&#xff09;&#xff0c;就在上一节的基础上加了两个PID。 二、PID控制 PID的好处就是可以不用动力学建模&#xff08;当…

个人的一个模拟面试java1

文章目录 列举 Java 集合框架中的几个主要接口和类解释 ArrayList 和 LinkedList 的区别什么是 HashCode 和 Equals 方法&#xff0c;为什么在覆盖 Equals 方法时通常也要覆盖 HashCode 方法&#xff1f;和equals()的区别equals() 方法在 String 类中是如何被覆盖的&#xff1f…

Vue-组件中的data

一个组件的data选项必须是一个函数。保证每个组件实例&#xff0c;维护独立的一份数据对象。如下图&#xff1a; 组件一旦封装好了&#xff0c;可以使用多次&#xff0c;比如数字框组件使用了三次&#xff1a; 每次创建新的组件实例&#xff0c;都会重新执行一次data函数&#…

52. 【Android教程】网页视图:WebView

在前面的章节我们所围绕的全部都是纯客户端开发&#xff0c;我们叫 Native 开发。这样的好处就是体验和性能会非常好&#xff0c;但是在实际的使用中我们会发现存在大量的 H5 页面。这样就可以结合 Native / H5 双端的优势完成一个混合开发&#xff0c;而在这种开发模式中首当其…

windows 环境下安装《车辆动态监控系统》支持JT808、JT1078、苏标主动安全设备接入

《车辆动态监控系统》下载安装部署包 开放端口 80/443/8800&#xff0c;web后台端口&#xff0c;nginx代理服务&#xff0c;nginx默认为8800端口8808&#xff0c;JT808专用端口6802&#xff0c;视频播放推流端口6891-6898&#xff0c;FTP端口6821&#xff0c;苏标主动安全附件…