springboot第56集:微服务框架,物联网IOT,SQL数据库MySQL底层,AOP收集业务操作日志架构周刊...

news2025/2/19 23:25:27

单点登录

1.配置代理信息

/*请求登陆的方法*/
"/modelLogin": {
  //本地服务接口地址,这是测试环境,正式环境需要更改下地址
  target: "http://127.0.0.1:6776/xxx-auth/",
  changeOrigin: true,
  pathRewrite: {
    "^/modelLogin": "",
  },
},


//异步进行登录
this.$store.dispatch("LoginPrammeSystem", loginForm).then(() => {
  //跳转到指定连接(正式环境需要更改地址)
   window.open("http://localhost:1889/");
});
06549dffa5dbc0a4961f27e75b586d0c.png
image.png
fcb9da287804b23eb9afc5de4f137777.png
image.png
b3d9e8789bb777cf9cc8616066379398.png
image.png
5db0aea8c53939e8aa15d63fe247405c.png
image.png
0a467c0879510a8cbe56413e2c5c2630.png
image.png

MySQL对于千万级的大表如何优化

优化你的sql、索引

B+树

sql优化

避免多表联合查询,优化难度大

设置合理的查询字段,避免多次回表

索引

建立合适的索引

避免索引失效

优点

解决读的性能瓶颈

缺点

缓存数据库一致性

缓存穿透

缓存雪崩

缓存击穿

架构复杂(高可用)

客户端直接连接 客户端直连方案,因为少了一层 proxy 转发,所以查询性能稍微好一点儿,并且整体架构简单,排查问题更方便。但是这种方案,由于要了解后端部署细节,所以在出现主备切换、库迁移等操作的时候,客户端都会感知到,并且需要调整数据库连接信息。中间件:ShardingSphere

带proxy 带 proxy 的架构,对客户端比较友好。客户端不需要关注后端细节,连接维护、后端信息维护等工作,都是由 proxy 完成的。但这样的话,对后端维护团队的要求会更高。而且,proxy 也需要有高可用架构。因此,带 proxy 架构的整体就相对比较复杂。中间件:ShardingSphere  、Atlas 、mycat

优点

分担主库的压力

缺点

从延迟,导致往主库写入的数据跟从库读出来的数据不一致

优点

优化单一表数据量过大而产生的性能问题

避免IO争抢并减少锁表的几率

缺点

主键避免重复(分布式Id)

跨节点分页、排序函数

数据多次扩展难度跟维护量极大

1e100c38108b19232c86d5fb403261a2.png
image.png
d097687ea2c80dfc056702f27789a22f.png
image.png
5486d01cd81a53d474394ceda572504f.png
image.png
c6e41abaad7f88336e13f52a07c4c2d0.png
image.png
95334123663c2d724e4922fe391717d0.png
image.png

mac安装Elasticsearch

使用的是2.3.2版本   下载的是tar版本的,然后解压。

cd elasticsearch-2.3.2

bin/elasticsearch

这时可能会报一个logs文件夹权限问题的错误

sudo chown -R dalaoHang logs

重启es

在页面访问http://localhost:9200/

安装一个可视化插件,elasticsearch-head

sudo bin/plugin install mobz/elasticsearch-head

安装成功后,重启es,访问http://localhost:9200/_plugin/head/

Linux文件结构与文件权限

d:代表当前文件为文件夹

l:代表当前文件为超连接文件

b:该文件提供为提供给系统存取的设备,例如你计算机的硬盘所存储的数据内容,可能都会保存在这个类型文件中

c:代表着连接到你计算机的一些硬件设备,例如鼠标、键盘等配置文件开头都是以c开头的

一个b打头的文件,这个文件是提供系统存储的数据且可能硬盘也可能是其他存储设备的

ll /dev/sda

c打头的文件则是硬件设备

而c打头的文件则是硬件设备,我们不妨键入 ll /dev查看一下 BUS总线,以及CPU等这些都是硬件设备

在Linux中隐藏文件大多以.开头

ls -al查看

1. /:根目录,与开机系统有关
2. /usr(unix software resource):与软件的安装和执行有关
3. /var(variable):与系统的运作有关

根目录下有个bin文件夹,我们日常使用Linux的时候都会用到ls、cat、touch、mkdir等基本操作命令都存放于bin目录下

接下来是/boot,这个目录存放的基本都是Linux开机会用到配置文件,像Linux kerner常用到的文件名 vmlinuz就会存放在这个文件夹下

接下来是/dev,这个文件夹基本存放的都是硬件设备,我们都知道Linux主张一切皆文件,所以所有的鼠标、键盘、硬盘的设备信息都是存放在这个文件夹下

/etc目录则是存放配置文件的地方,常见我们的用户文件/etc/passwd,以及密码文件/etc/shadow,还有系统文件/etc/rc等都会存放在这个目录下。

mnt存放挂载相关。

lib存放库函数相关、

sbin存放开机、系统还原、修复等众多指令。

tmp存储临时文件习惯。

/usr/lib与根目录的lib功能相同,存放的基本是lib目录下的软链接。

/usr/local/则是FHS希望用户将下载的软件都放到这个目录下统一管理

/usr/sbin同样也是将根目录的sbin目录下的指令软链接到此。

/var/lock存放的则是被某个程序锁定的文件,已确保其他程序不会同时使用到这个文件

/var/lib存放的则是会临时改变的库文件

文件权限

r代表读权限。

w代表写权限。

x代表执行权限。

# 进入tmp目录
 cd /tmp/
 
 # 创建testDir文件夹
mkdir testDir

# 设置这个文件夹 所属者有所有权限 所属组和其他用户只有读和写权限
chmod 766 testDir/

MySQL 并不是跳过 OFFSET 行,而是取 OFFSET+N 行,然后放弃前 OFFSET 行,最后返回 N 行,当 OFFSET 特别大的时候,效率就非常的低下

spring security支持的oauth resource server自带BearerTokenAuthenticationFilter去校验jwt,所以简单配置一个jwtDecoder就可以实现完全相同的功能

整合SpringSecurity和JWT实现登录认证和授权

SpringSecurity是一个强大的可高度定制的认证和授权框架,对于Spring应用来说它是一套Web安全标准。SpringSecurity注重于为Java应用提供认证和授权功能,像所有的Spring项目一样,它对自定义需求具有强大的扩展性。

其核心就是一组过滤器链,在spring security中一种过滤器处理一种认证方式,项目启动后将会自动配置

JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。总结来说,JWT只是一个生成token的机制。

  1. JWT的组成

JWT token的格式:header.payload.signature 可以在该网站上获得解析结果:jwt.io/

header中用于存放签名的生成算法

payload中用于存放用户名、token的生成时间和过期时间

signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败

JWT实现认证和授权的原理

用户调用登录接口,登录成功后获取到JWT的token;

之后用户每次调用接口都在http的header中添加一个叫Authorization的头,值为JWT的token;

后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息,从而实现认证和授权。

<!--SpringSecurity依赖配置-->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JWT(Json Web Token)登录支持-->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt</artifactId>
 <version>0.9.0</version>
</dependency>
jwt:
  tokenHeader: X-Token #JWT存储的请求头
  tokenHead: Bearer  #令牌前缀
  secret: xx-admin-secret #JWT加解密使用的密钥
  expiration: 604800 #JWT的超期限时间秒(60*60*24)
@Slf4j
@Component
public class JwtTokenUtil {

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";

    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expire;


    /**
     * 从token中获取登录用户名
     */
    public String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username =  claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    /**
     * 校验token
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * 根据用户信息生成token
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getClaimsFromToken(token).getExpiration();
        return expiredDate.before(new Date());
    }

    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                //签名算法
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expire * 1000);
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.info("JWT格式验证失败:{}",token);
        }
        return claims;
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAccessDeniedHandler customAccessDeniedHandler;
    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
    @Autowired
    private XlUserService xlUserService;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable()// 由于使用的是JWT,我们这里不需要csrf
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 允许对于网站静态资源的无授权访问
                .antMatchers(HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-resources/**",
                        "/v2/api-docs/**"
                )
                .permitAll()
                // 对登录注册要允许匿名访问
                .antMatchers("/ucenter/xl-user/login", "/ucenter/xl-user/register")
                .permitAll()
                //跨域请求会先进行一次options请求
                .antMatchers(HttpMethod.OPTIONS)
                .permitAll()
                .anyRequest()// 除上面外的所有请求全部需要鉴权认证
                .authenticated();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(customAccessDeniedHandler)
                .authenticationEntryPoint(customAuthenticationEntryPoint);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        //获取登录用户信息
        return username -> {
            XlUser user = xlUserService.getUserByCode(username);
            if (user != null) {
                List<XlResource> permissionList = xlUserService.getResourceList(user.getUserId());
                return new JwtUser(user,permissionList);
            }
            throw new UsernameNotFoundException("用户名或密码错误");
        };
    }

    /**
     * 装载BCrypt密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * JWT filter
     */
    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
        return new JwtAuthenticationTokenFilter();
    }
}

编写service实现:

@Slf4j
@Service
public class XlUserServiceImpl extends ServiceImpl<XlUserMapper, XlUser> implements XlUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;


    @Override
    public XlUser register(XlUser user) {
        XlUser newXlUser = new XlUser();
        BeanUtils.copyProperties(user, newXlUser);
        //查询是否有相同用户名的用户
        List<XlUser> xlUsers = this.baseMapper.selectList(new LambdaQueryWrapper<XlUser>().eq(XlUser::getUserCode,newXlUser.getUserCode()));
        if (CollectionUtil.isNotEmpty(xlUsers)) {
            return null;
        }
        //将密码进行加密操作
        String encodePassword = passwordEncoder.encode(user.getPassWord());
        newXlUser.setPassWord(encodePassword);
        this.baseMapper.insert(newXlUser);
        return newXlUser;
    }

    @Override
    public String login(String username, String password) {
        String token = null;
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return token;
    }

    @Override
    public List<XlResource> getResourceList(Long userId) {
        return this.baseMapper.getResourceList(userId);
    }

测试访问需要权限的接口

@PreAuthorize("hasAuthority('read')")
@ApiOperation("获取用户所有可访问的资源")
81862b8e864bb5817f5808324772ff2f.png
image.png

mysql优化 | 存储引擎,建表,索引,sql的优化建议

存储引擎

mysql中查看支持的引擎的sql:

show engines;

日常工作中使用较多的存储引擎对比:InnoDB,MyISAM

InnoDB   存储限制   64T   支持事务 支持索引 支持数据缓存 支持外键

MyISAM   存储限制   256T   支持索引 支持全文索引

从MySQL5.6版本开始InnoDB已经支持创建全文索引了

innodb

支持提交、回滚和崩溃恢复能力的事物安全(ACID),支持行锁,支持外键完整性约束

适合场景

需要事务处理

表数据量大,高并发操作

MyISAM

MyISAM存储引擎提供了高速检索和存储的能力,支持全文索引

适合场景

很多count计算的

查询非常频繁的

建表原则

在建表的时候尽量遵循以下原则

尽量选择小的数据类型,数据类型选择上尽量tinyint(1字节)>smallint(2字节)>int(4字节)>bigint(8字节),比如逻辑删除yn字段上(1代表可用,0代表)就可以选择tinyint(1字节)类型

尽量保证字段数据类型长度固定

尽量避免使用null,使用null的字段查询很难优化,影响索引,可以使用0或''代替

避免宽表,能拆分就拆分,一个表往往跟一个实体域对应,就像设计对象的时候一样,保持单一原则

尽量避免使用text和blob,如果非使用不可,将类型为text和blob的字段在独立成一张新表,然后使用主键对应原表

禁止使用float或double类型,这个坑超大,float或double存在精度问题,在进行比较或者加减操作的时候会丢失精度导致数据异常,凡是使用float或double类型的时候考虑下可不可使用int或bigint代替。比如金额,以元为单位使用float或double类型的时候,可以考虑以分为单位使用int,bigint类型代替,然后由业务代码进行单位的转换。

每张表都加上createUser,createTime.updateUser,updateTime字段

起名字要规范,包括:库名,表名,字段名,索引名

查询频繁使用的字段记得加索引

尽量避免使用外键,不用外键约束,性能更高,然后数据的完整性有程序进行管理

如果表的数量可以预测到非常大,最好在建表的时候,就进行分表,不至于一时间数据量非常大导致效率问题

索引

索引是为来加速对表中数据行中的检索而创建的一种分散的数据结果,是针对表而建立的,它是由数据页面以外的索引页面组成,每个索引页中的行都含有逻辑指针,以便加速检索物理数据,创建索引的目的在于提高查询效率,innodb的索引都是基于b tree实现的

索引类型

普通索引:最基本的索引,无限制

//1
create index idx_username on sys_user(user_name(32));

// 2
alter table sys_user add index idx_username(user_name(32));

主键索引:一个表只能有一个主键索引,且不能为空

一般建表时同时创建了主键索引

create table `sys_user` (
 `id` int(11) not null auto_increment,
 `user_name` varchar(32) default null,
 `pass_word` varchar(32) default null,
 `token` varchar(32) default null,
 `yn` smalliint(6) default null,
 primary key (`id`)
) engine=InnoDB auto_increment=348007 default charset=utf8;

唯一索引:与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一

CREATE UNIQUE INDEX idx_token ON sys_user(token_expire)

组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合

alter table sys_user add index idx_un_te (user_name(32), token_expire);

全文索引:用来查找文本中的关键字,而不是直接与索引中的值相比较。只有char、varchar,text 列上可以创建全文索引

CREATE FULLTEXT INDEX idx_ ON sys_user(pass_word)

总之使用索引的时候,需要考虑的地方比较多,但是归根结底就是查询尽量走索引,走索引尽量避免回表或减少回表次数

创建使用索引的原则

索引的字段尽量要小,根据索引查询数据的快慢取决于b tree的高度,当数据量恒定的时候,字节越少,存的索引的数量就越多,树的高度就越会越低

比如:设置varchar(10),则这个索引建立的时候只会存字段前10个字节,字段设置的字节数比较小可能会导致索引查出来的数据多,进而进行回表,导致性能下降,所以字段设置为多少还是要自己斟酌一下

遵循索引的最左匹配原则

注意使用like的时候尽量不要使用“%a%”,这样的不走索引,可以使用“a%”,走索引

不要在索引的列上进行计算,比如 select * from sys_user where token_expire+1 = 10000,这样的语句 不会走有索引

什么样的字段建索引,就是那种频繁在where,group by,order by中出现的列,最好加上索引

使用联合索引的时候尽量考虑到索引下推优化

对于使用or的条件,需要or左右的条件都是索引才会走索引,否则走全表扫描,可以考虑使用union代替

避免使用select *,对于只需要查询主键或者where 条件中只有索引的字段, 这时会走覆盖索引建少回表次数

sql语句中避免隐式转换,在MySQL中,字符串和数字做比较的话,是将字符串转换成数字,如字段是varchar类型,但是入参是int类型,即便字段有索引也不会走,因为这里会进行一次隐式转换

索引的缺点

虽然索引的可以提高查询的效率,但是在进行insert,update,和delete的时候会降低效率,因为在保存数据的同时也会去保存索引。

不要在一个表里建过多的索引,问题跟上面一样,在操作数据的时候效率降低,而且数据量少的表要看情况建索引,如果建索引跟没建索引的效果差不多少的情况下就不要建索引了,如果是数据量大的表,就需要建索引去优化查询效率。

explain分析sql

可以使用explain去分析sql的执行情况,比如

explain select * from sys_user where token_expire = 10000;

在阿里的开发手册中提到过,sql性能优化的标准:至少要达到range,要求ref级别,如果可以是consts最好

consts 是指单表中最多只有一个匹配行(主键或唯一索引)

ref 指的是使用普通索引

range 是指对索引进行范围查询

sql优化

关于sql语句的优化主要是两方面,一个是在建sql的时候需要注意的问题,另一个就是在发现有慢sql的时候可以根据不同情况进行分析,然后优化sql

现在innodb已经支持全文索引了!

从MySQL5.6版本开始InnoDB支持创建全文索引

优化的建议

查询的时候一定要记得使用limit进行限制

对于结果只需要一条数据的查询用limit 1进行限制

使用count(*)或count(1)来统计行数来查询,使用count(列)的时候,需要在查看列中这个是否为null,不会统计此列为null的情况,而且mysql已经对count(*)做了优化

不要使用select * 来查数据,使用select 需要的列名,这样的方式去查询

使用join链接代替子查询

不要使用外键,外键的约束可以放在程序里解决

控制一下in操作的集合数量,不要太大了

针对慢查询使用explain去分析原因,然后优化sql,让其尽量走索引

a0dab79ba010c31960114850a228e976.png
image.png
d494922204b55a65f53fbce98b6078a7.png
image.png
2b8854fac9b7d1ffbe936c4cf6bfb49d.png
image.png
365012958e6373c6c086654897ad98ab.png
image.png
ce18e2c0db13e3ac0c6a335411a28d7c.png
image.png
c3a57ddc511f3ac8d01e39e81d0e42e9.png
image.png
e6b63d30cdfd9fd99277a60bad06f9b6.png
image.png
297df327e4892d547c0daca507274849.png
image.png
c782f39cc97141698cd6039e1918bd89.png
image.png
4ac90a271c80e2ec9395d142bada54f9.png
image.png
c4fac5cdc988525cdd3f9865fcba5660.png
image.png
c6f3f60918b9b28680cdf875a6a65fd8.png
image.png
e4448282b352a9efee5ace038979cf0e.png
image.png
aef5737563187f4a8d503838bfac36dc.png
image.png
099530076a4baefa062ad3c578fbf044.png
image.png

spring boot集成mqtt协议发送和订阅数据

@Autowired
private MqttGatewayComponent mqttGatewayComponent;

//发送字符串或json字符串,到指定的topic
mqttGatewayComponent.sendToMqtt("json string", "data/abcd");

/**
 * @desc MQTT发送网关
 */
@Component
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGatewayComponent {

    void sendToMqtt(String data);

    void sendToMqtt(String payload, @Header(MqttHeaders.TOPIC) String topic);

    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);

}

// 通过通道获取数据 订阅的数据
// 配置监听的 topic 支持通配符
// 发送通道配置 默认主题

@Bean
public MqttConnectOptions getMqttConnectOptions(){
    // MQTT的连接设置
    MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
    // 设置连接的用户名
    mqttConnectOptions.setUserName(username);
    // 设置连接的密码
    mqttConnectOptions.setPassword(password.toCharArray());
    // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
    // 把配置里的 cleanSession 设为false,客户端掉线后 服务器端不会清除session,
    // 当重连后可以接收之前订阅主题的消息。当客户端上线后会接受到它离线的这段时间的消息
    mqttConnectOptions.setCleanSession(true);
    // 设置发布端地址,多个用逗号分隔, 如:tcp://111:1883,tcp://222:1883
    // 当第一个111连接上后,222不会在连,如果111挂掉后,重试连111几次失败后,会自动去连接222
    mqttConnectOptions.setServerURIs(hostUrl.split(","));
    // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
    mqttConnectOptions.setKeepAliveInterval(20);
    mqttConnectOptions.setAutomaticReconnect(true);
    // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
    mqttConnectOptions.setWill(willTopic, willContent.getBytes(), 2, false);
    mqttConnectOptions.setMaxInflight(1000000);
    return mqttConnectOptions;
}

说明:由于测试机的端口限制,单机最多也就65553个端口了,所以理论上jmeter最大也就可以模拟6万个连接(系统本身也会有很多服务占用端口)

使用虚拟机理论上是可以让测试机端口无限的,前提性能跟得上。但实际中我们发现,单台测试机跑jmeter到3万个连接,其实已经是极限了(内存和cpu性能问题)

netty的大型响应式分布式物联网智能家电系统,十万长连接

压测效果 单机下压测效果 稳定6万连接(自身配置限制 i5 6300hq+16G)

压测效果 集群模式下(两节点)压测效果 达成12万连接

da3db121f0fa16d455dc5c7d97145ecc.png
image.png

不想再用AOP收集业务操作日志

业务操作日志是软件系统中用于记录和跟踪用户对业务数据执行的操作的日志。这些日志提供了对系统活动的见解,有助于审计、监控、分析和重构业务流程。

作为一名对Spring重度使用者😎😎,基于上面的需求目标马上想到了基于AOP切面+注解的传统方案,AOP切面和注解来设计业务操作日志是一种非常自然和高效的方法,我们基于AOP切面和注解的方法来实现我们系统中的业务操作日志记录,这种方案允许我们以最小的侵入性来捕获核心业务操作,并在执行前后自动记录相关数据。通过定义注解,我们可以轻松地标记那些需要记录日志的业务方法。

1.1 定义日志注解

首先,你需要定义一个或多个注解,用于标记哪些方法需要记录业务操作日志。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
    String value() default "";
    // 可以添加更多的配置属性,如操作类型、级别等
}

1.2 创建AOP切面

接下来,创建一个切面类来处理带有@Loggable注解的方法。

@Aspect
@Component
public class LoggingAspect {

    @Around("@annotation(loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object proceed = joinPoint.proceed(); // 执行目标方法

        long executionTime = System.currentTimeMillis() - start;
        
        // 记录日志的逻辑
        logger.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        
        return proceed;
    }
}

1.3 配置Spring AOP+标记注解

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // 可能还需要其他的配置或Bean定义
}

public class SomeService {
    
    @Loggable
    public void someBusinessMethod(Object someParam) {
        // 业务逻辑
    }
}

1.4 定义日志记录逻辑

在切面中,我们就可以自定义日志记录逻辑,可以记录更多的上下文信息,如方法参数、返回值、执行时间、异常信息等。

@Autowired
private Logger logger; // 例如,通过SLF4J获取的Logger

@Around("@annotation(loggable)")
public Object logBusinessOperation(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
    // 方法执行前的逻辑,例如记录开始时间、方法参数等
    long start = System.currentTimeMillis();
    try {
        Object result = joinPoint.proceed(); // 执行目标方法
        // 方法执行后的逻辑,例如记录结束时间、返回值等
        return result;
    } catch (Exception e) {
        // 异常处理逻辑,如记录异常信息
        throw e;
    } finally {
        long executionTime = System.currentTimeMillis() - start;
        // 构建日志信息并记录
        logger.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);
    }
}

•业务操作场景划分:切面的定义和使用都是非业务化的,所以无法感知到新的业务操作范围和业务的定义划分边界是如何处理;

•级联操作断档:当业务操作是设计多表或者多个服务间的调用串联时,切面只能单独记录每个服务方法级别的数据信息,无法对调用链的部分进行业务串联;

@Repeatable(LogRecords.class)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogRecord {
    String success();

    String fail() default "";

    String operator() default ""; //业务操作场景人

    String type(); // 业务场景 模块范围

    String subType() default ""; //业务子场景,主要是模块下的功能范围

    String bizNo(); //业务场景的业务编号,

    String extra() default "";//一些操作的扩展操作

    String actionType(); //业务操作类型,比如编辑、新增、删除
}

Binlog大家都不陌生,是数据库中二进制格式的文件,用于记录用户对数据库更新的SQL语句信息,例如更改数据库表和更改内容的SQL语句都会记录到binlog里。那么Binlog能用来记录业务层面的数据变化内容吗?

3.1 Binlog

Binlog大家都不陌生,是数据库中二进制格式的文件,用于记录用户对数据库更新的SQL语句信息,例如更改数据库表和更改内容的SQL语句都会记录到binlog里。那么Binlog能用来记录业务层面的数据变化内容吗?

•问题1:无法对多表存在级联保存和更新的数据进行非常好的兼容支持,因为本身binlog数据是无序的,并且如果上游数据的操作不是包裹在一个事务中,也很难处理

•问题2:关于更新人的问题,系统进行更新时如果未手动更新对应操作人,则系统无法识别,需要上游做对应场景的统一改造,但从系统承接来看,本身系统的操作人就是要跟着业务操作一起进行联动的

MVCC

MVCC最大的好处是读不加锁,读写不冲突,在读多写少的系统应用中,读写不冲突是非常重要的,可极大提升系统的并发性能,这也是为什么现阶段几乎所有的关系型数据库都支持 MVCC 的原因,目前MVCC只在 Read Commited 和 Repeatable Read 两种隔离级别下工作。它是通过在每行记录的后面保存两个隐藏列来实现的,这两个列, 一个保存了行的创建时间,一个保存了行的过期时间, 存储的并不是实际的时间值,而是系统版本号。MVCC在mysql中的实现依赖的是undo log与read view。

read view

在 MVCC 并发控制中,读操作可以分为两类: 快照读(Snapshot Read)与当前读 (Current Read)。

•快照读:读取的是记录的快照版本(有可能是历史版本)不用加锁(select)。

•当前读:读取的是记录的最新版本,并且当前读返回的记录,都会加锁,保证其他事务不会再并发修改这条记录(select… for update 、lock或insert/delete/update)。

redo log

redo log叫做重做日志。mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Buffer Pool(缓冲池)里,当作缓存来用以提升性能,使用后台线程去做缓冲池和磁盘之间的同步。那么问题来了,如果还没来及的同步的时候宕机或断电了怎么办?这样会导致丢部分已提交事务的修改信息!所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后再读取redo log恢复最新数据。redo log是用来恢复数据的,保障已提交事务的持久化特性。

undo log

undo log 叫做回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化。为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。undo log 记录事务修改之前版本的数据信息,假如由于系统错误或者rollback操作而回滚的话可以根据undo log的信息来进行回滚到没被修改前的状态。undo log是用来回滚数据的,保障未提交事务的原子性。

假设 F1~F6 是表中字段的名字,1~6 是其对应的数据。后面三个隐含字段分别对应该行的隐含ID、事务号和回滚指针

具体的更新过程如下:

假如一条数据是刚 INSERT 的,DB_ROW_ID 为 1,其他两个字段为空。当事务 1 更改该行的数据值时,会进行如下操作

•用排他锁锁定该行,记录 Redo log;

•把该行修改前的值复制到 Undo log

•修改当前行的值,填写事务编号,并回滚指针指向 Undo log 中修改前的行。

如果再有事务2操作,过程与事务 1 相同,此时 Undo log 中会有两行记录,并且通过回滚指针连在一起,通过当前记录的回滚指针回溯到该行创建时的初始内容  这里的undolog不会一直增加,purge thread在后面会进行undo page的回收,也就是清理undo log。

JDBC->jre system library -> rt.jar(sql,Connection.class, Driver.class,)

// 创建数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/easyflow", "root", "12345678");
// 自动提交设置
connection.setAutoCommit(false);
// 只读设置
connection.setReadOnly(false);
// 事务隔离级别设置
connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
// 创建查询语句
PreparedStatement statement = connection.prepareStatement("update config set cfg_value='1' where id=11111");
// 执行SQL
int num = statement.executeUpdate();
System.out.println("更新行数:" + num);
// 事务提交
connection.commit();

Mybatis事务相关

Mybatis核心是提供了sql查询方法、结果集与应用方法及对象之间的映射关系,便于开发人员进行数据库操作。

  • 事务transaction

  • JDBC对接

  • 数据源datasource

  • 反射reflection

  • IO封装

  • 游标cursor

  • 异常exceptions

  • 字节码处理javassist

  • 会话session

  • 执行器executor

  • 映射mapping

  • 映射binding

  • 构建器builder

  • 模版引擎ognl

  • 解析器parsing

  • 脚本处理scripting

  • 缓存cache

  • 日志logging

  • 插件plugin

  • 类型type

  • 注解annotation

  • java版本lang

加群联系作者vx:xiaoda0423

仓库地址:https://github.com/webVueBlog/JavaGuideInterview

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

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

相关文章

MATLAB知识点:ismembertol函数(★★☆☆☆)考虑了一定的容差的ismember函数

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

人工智能学习与实训笔记(八):百度飞桨套件使用方法介绍

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 本篇目录 八、百度飞桨套件使用 8.1 飞桨预训练模型套件PaddleHub 8.1.1 一些本机CPU可运行的飞桨预训练简单模型&#xff08;亲测可用&#xff09; 8.1.1.1 人脸检测模型 8.1.1.2 中文分词模型 8.1…

【开源】SpringBoot框架开发创意工坊双创管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、系统展示四、核心代码4.1 查询项目4.2 移动端新增团队4.3 查询讲座4.4 讲座收藏4.5 小程序登录 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的创意工坊双创管理…

Portraiture插件2024官方版功能主要介绍

Portraiture插件的功能主要包括&#xff1a; 一键平滑处理&#xff1a;可以对眉毛、头发等毛发材质进行一键平滑处理&#xff0c;使其质感显得更加光鲜亮丽。同时&#xff0c;这种处理还能保证原有图像的画质。使用吸管工具添加肤色蒙版&#xff1a;这个工具可以吸取人物面部的…

php基础学习之匿名函数

匿名函数 描述 允许临时创建一个没有指定名称的函数&#xff0c;使用匿名函数可以方便地创建一次性或临时的功能块。 语法 function (){ //函数体 }; 样例 &#xff08;注&#xff1a;匿名函数定义后&#xff0c;需要将其赋值给一个变量&#xff0c;然后通过该变量来调用 这种调…

【BUG】段错误

1. 问题 8核工程&#xff0c;核4在运行了20分钟以上&#xff0c;发生了段错误。 [C66xx_4] A00x53 A10x53 A20x4 A30x167e A40x1600 A50x850e2e A60x845097 A70xbad9f5e0 A80x0 A90x33 A100x53535353 A110x0 A120x0 A130x0 A140x0 A150x0 A160x36312e35 A170x20 A180x844df0 …

15-树-路径总和

这是树的第15篇算法&#xff0c;力扣链接。 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff…

C++学习Day05之强化训练---数组类封装

目录 一、程序及输出1.1 数组类头文件1.2 数组类.cpp1.3 主程序 二、分析与总结 一、程序及输出 1.1 数组类头文件 myArray.h #include<iostream> using namespace std;class MyArray { public:MyArray(); //默认构造 可以给100容量MyArray(int capacity); //有参构造…

OpenAI全新发布的Sora,到底意味着什么?

16日凌晨&#xff0c;OpenAI发布了文本视频的工具&#xff08;text-do-video&#xff09;Sora&#xff0c;整个世界再次被震撼。 Sora的出现&#xff0c;到底意味着什么&#xff1f; 目录 Sora的背景与概述Sora是什么&#xff1f;能为我们做些什么&#xff1f;存在的一些问题 文…

目前2024年4核8G云服务器租用价格,阿里云PK腾讯云

4核8G云服务器多少钱一年&#xff1f;阿里云ECS服务器u1价格955.58元一年&#xff0c;腾讯云轻量4核8G12M带宽价格是646元15个月&#xff0c;阿腾云atengyun.com整理4核8G云服务器价格表&#xff0c;包括一年费用和1个月收费明细&#xff1a; 云服务器4核8G配置收费价格 阿里…

c++编译原理LR分析程序 代码+报告

编译原理实验报告 目 录 目录 1 实验目的 2 实验内容 3 实验要求 4 实验原理 4.1 TPL语言语法分析的文法 4.2设计思路 4.3LR分析表 5. 数据结构、函数说明和主要算法 5.1 主要的数据结构 5.2 所有函数的说明 5.3 主要算法 6. 实现与测试 6.1 源程序1测试 7 …

快速搞懂时间序列数据平稳检验

在对时间序列数据进行分析预测时&#xff0c;平稳时间序列数据预测效果更好。所以首先要检测数据是否平稳&#xff0c;没有趋势的时间序列数据&#xff0c;我们称为平稳的&#xff0c;即随着时间的推移&#xff0c;表现出恒定的方差&#xff0c;具有恒定的自相关结构。本文介绍…

Vue实现多个input输入,光标自动聚焦到下一个input

遇到一个需求&#xff0c;需要实现和移动端短信输入一样&#xff0c;输入内容后&#xff0c;光标会进入下一个输入框 需要用到2个事件 keydown事件发生在键盘的键被按下的时候 keyup 事件在按键被释放的时候触发 <template><div class"box"><el-fo…

读书笔记之《我的大脑好厉害》:脑科学启蒙

《我的大脑好厉害》的作者是赵思家&#xff0c;于 2022年出版。 赵思家是牛津大学神经科学博士后&#xff0c;科普作者&#xff0c;知乎神经科学领域大V&#xff0c;46万关注&#xff0c;擅长为充满好奇的读者提供新鲜、有趣又不失严谨的神经科学知识。赵思家2013年毕业于伦敦大…

Linux CentOS stream 9 安装docker

在计算机技术中,虑拟化是一种资源管理技术,是将计算机的各种实体资源(CPU、内存、磁盘空间、网络适配器等),予以抽象、转换后呈现出来并可供分区、组合为一个或多个电脑配置环境。 目前,大多数服务器的容量的利用率不足15%,这导致服务器数量激增以及增加了复杂性。服务…

C语言系列(所需基础:大学C语言及格)-1-编译器/简单的求和代码/数据类型/变量的分类/变量的作用域和生命周期

文章目录 一、编译器&#xff08;使用在线编译器&#xff09;二、简单的求和代码三、数据类型四、变量的分类五、变量的作用域和生命周期 一、编译器&#xff08;使用在线编译器&#xff09; 为了方便&#xff0c;我使用的是在线的C语言编译器进行程序的运行。 链接&#xff1…

Vue的一些基础设置

1.浏览器控制台显示Vue 设置找到扩展&#xff0c;搜索Vue 下载这个 然后 点击扩展按钮 点击详细信息 选择这个&#xff0c;然后重启一下就好了 ——————————————————————————————————————————— 2.优化工程结构 src的components里要…

Tomcat要点总结

一、Tomcat 服务中部署 WEB 应用 1.什么是Web应用 &#xff08;1&#xff09; WEB 应用是多个 web 资源的集合。简单的说&#xff0c;可以把 web 应用理解为硬盘上的一个目录&#xff0c; 这个目录用于管理多个 web 资源。 &#xff08;2&#xff09;Web 应用通常也称之为…

[嵌入式系统-24]:RT-Thread -11- 内核组件编程接口 - 网络组件 - TCP/UDP Socket编程

目录 一、RT-Thread网络组件 1.1 概述 1.2 RT-Thread支持的网络协议栈 1.3 RT-Thread如何选择不同的网络协议栈 二、Socket编程 2.1 概述 2.2 UDP socket编程 2.3 TCP socket编程 2.4 TCP socket收发数据 一、RT-Thread网络组件 1.1 概述 RT-Thread 是一个开源的嵌入…

微星B85 PCMATE 配合 E3-1230V3锁全核3.7

参考&#xff1a; Non-K超频&#xff0c;i5-12490F 微星 MAG B760M MORTAR MAX WIFI DDR4 超频分享 - 知乎 微星科技 | 微星科技 ring ratio 的说明 英特尔CPU 可以调整的ring ratio (ring 频率&#xff09;是什么&#xff1f; - 知乎 1. 主要调节的是 CPU倍频&#xf…