开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Linux系列学习第六天——一步一步教会你如何使用Linux

发表于 2021-08-22

“这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战”

茫茫人海千千万万,感谢这一秒你看到这里。希望我的文章对你有所帮助!

愿你在未来的日子,保持热爱,奔赴山海!

Linux的高级命令

昨天我们已经将Linux的一些基础命令全部学习完毕,接下来我们即将进入一个新的阶段,对于Linux的高级命令的学习!!!

今天我们来着重学习Linux中对用户一些管理命令。

Linux的用户管理

  1. Linux系统是一个多用户多任务的操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统
  2. Linux的用户需要至少要属于一个组。
  3. root 是超级管理员用户。

添加用户

命令:

useradd [选项] 用户名:创建一个用户

password 用户名:给用户名指定密码

参数详情介绍:

  • -d :给新创建的用户指定家目录。

具体演示:

删除用户

命令:

userdel [选项] 用户名:删除一个用户

参数详情介绍:

  • -r:删除用户并会删除用户家目录

具体演示:

查询用户信息

命令:

id 用户名:查询用户信息

具体演示:

切换用户

在操作 Linux中,如果当前用户的权限不够,可以通过su-指令,切换到高权限用户,比如root。

命令:

su - 用户名:切换用户

具体演示:

注意:

  1. 从权限高的用户切换到权限低的用户,不需要输入密码,反之需要。
  2. 当需要返回到原来用户时,使用exit指令

对组操作

命令:

groupadd 用户组 :增加组

groupdel 用户组 :删除组

useradd -g 用户组 用户名 :增加用户时加组

usermod -g 用户组 用户名 : 修改用户的组

具体演示:

总结

相信各位看官都对Linux系统的用户管理相关命令有了一定了解,期待等待下一章的Linux中的高级命令中的文件权限管理的学习吧!

当然还有更多命令等着下次一起看吧!欢迎期待下一章的到来!

让我们也一起加油吧!本人不才,如有什么缺漏、错误的地方,也欢迎各位人才大佬评论中批评指正!当然如果这篇文章确定对你有点小小帮助的话,也请亲切可爱的人才大佬们给个点赞、收藏下吧,一键三连,非常感谢!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

💖10分钟认识RocketMQ!想进阿里连这个都不会?1️⃣

发表于 2021-08-22

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

🌈往期回顾

**感谢阅读,希望能对你有所帮助,博文若有瑕疵请在评论区留言或在主页个人介绍中添加我私聊我,感谢每一位小伙伴不吝赐教。我是XiaoLin,既会写bug也会唱rap的男孩**
  • 💖5分钟带你自定义镜像和安装软件💖Docker系列入门教程
  • 💖5分钟带你认识Docker核心组件💖Docker系列入门教程
  • 💖5分钟带你跑一跑Docker💖Docker系列入门教程

一、消息中间件简介

1.1、什么是什么MQ

MQ(Message Queue)是一种跨进程的通信机制,用于传递消息。通俗点说,就是一个先进先出的数据结构。有点像队列。

image-20210511204821377

1.2、MQ的应用场景

1.2.1、异步解耦

最常见的一个场景是用户注册后,需要发送注册邮件和短信通知,以告知用户注册成功。传统的做法如下:

image-20210511205203724

此架构下注册、邮件、短信三个任务全部完成后,才返回注册结果到客户端,用户才能使用账号登录。但是对于用户来说,注册功能实际只需要注册系统存储用户的账户信息后,该用户便可以登录,而后续的注册短信和邮件不是即时需要关注的步骤。

所以实际当数据写入注册系统后,注册系统就可以把其他的操作放入对应的消息队列 MQ 中然后马上返回用户结果,由消息队列 MQ 异步地进行这些操作。架构图如下:

image-20210511205251775

异步解耦是消息队列 MQ 的主要特点,主要目的是减少请求响应时间和解耦。主要的使用场景就是将**比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列**。同时,由于使用了消息队列MQ,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦合。

1.2.2、流量削峰

流量削峰也是消息队列 MQ 的常用场景,一般在**秒杀或团队抢购(高并发)活动中**使用广泛。


在秒杀或团队抢购活动中,由于用户请求量较大,导致流量暴增,秒杀的应用在处理如此大量的访问流量后,下游的通知系统无法承载海量的调用量,甚至会导致系统崩溃等问题而发生漏通知的情况。为解决这些问题,可在应用和下游通知系统之间加入消息队列 MQ。

image-20210511205646128

秒杀处理流程如下所述:
  1. 用户发起海量秒杀请求1到秒杀业务处理系统。
  2. 秒杀处理系统按照秒杀处理逻辑将满足秒杀条件的请求1发送到消息队列MQ。
  3. 下游的通知系统订阅1消息队列MQ的秒杀相关消息,再将1秒杀成功的消息发送到相关用户。
  4. 用户收到秒杀成功的通知。

1.3、常见的MQ产品

目前业界有很多MQ产品,比较出名的有:ZeroMQ、RabbitMQ、ActiveMQ、RocketMQ、Kafka。

1.3.1、ZeroMQ

号称最快的消息队列系统,尤其针对大吞吐量的需求场景。扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的重新封装,如果做为消息队列使用,需要开发大量的代码。ZeroMQ仅提供非持久性的队列,也就是说如果down机,数据将会丢失。

1.3.2、RabbitMQ

使用erlang语言开发,性能较好,适合于企业级的开发。但是不利于做二次开发和维护。

1.3.3、ActiveMQ

历史悠久的Apache开源项目。已经在很多产品中得到应用,实现了JMS1.1规范,可以和spring-jms轻松融合,实现了多种协议,支持持久化到数据库,对队列数较多的情况支持不好。

1.3.4、RocketMQ

阿里巴巴的MQ中间件,由java语言开发,性能非常好,能够撑住双十一的大流量,而且使用起来很简单。

1.3.5、Kafka

Kafka是Apache下的一个子项目,是一个高性能跨语言分布Publish/Subscribe消息队列系统,相对于ActiveMQ是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。

二、RocketMQ入门

2.2、简介

RocketMQ是阿里巴巴开源的分布式消息中间件,现在是Apache的一个顶级项目。在阿里内部使用非常广泛,已经经过了"双11"这种万亿级的消息流转。

2.3、下载

RocketMQ官网

github

2.4、Windows安装

下载

我们先从[官网](http://rocketmq.apache.org/release_notes/release-notes-4.3.0/)去选择Binary下载,Binary代表是二进制版本,可以直接使用。

img

解压

image-20210511211315705

  • bin:启动脚本,包括shell脚本和CMD脚本
  • conf:实例配置文件 ,包括broker配置文件、logback配置文件等
  • lib:依赖jar包,包括Netty、commons-lang、FastJSON等

配置环境环境:ROCKETMQ_HOME

在电脑高级配置(配置jdk那边)配置环境变量,变量名为:`ROCKETMQ_HOME`,值为:MQ解压路径\MQ文件夹名。

image-20210511211545628

修改配置文件

进入RocketMQ的conf文件,里面有一个`broker.conf`配置文件。在文件末尾添加两行。(输入的时候建议将中文去掉)
1
2
markdown复制代码enablePropertyFilter=true <!--支持标签过滤-->
namesrvAddr=127.0.0.1:9876 <!--server服务的地址-->

image-20210511211938594

启动nameserver

在MQ解压文件的bin目录下进入CMD的1命令行,执执行命令(或者双击**mqnamesrv.cmd**),启动nameserver,**成功后会弹出提示框,不要关闭。**
1
2
shell复制代码# 执行命令
start mqnamesrv.cmd

image-20210511212338665

image-20210511212259441

启动broker

在bin目录下cmd进入命令行模式,执行命令,启动broker。**成功后不要关闭**。
1
shell复制代码mqbroker.cmd -c ../conf/broker.conf

image-20210511212532221

MQ可视化工具安装

可视化工具下载地址,密码6666

**这个jar一定要JDK8的环境**!!下载后修改`application.properties`文件,修改自己启动的端口和服务的ip+端口。集群按钮1那里出现了版本号说明启动成功!

image-20210511213950156

2.4、Linux安装

这里使用的不是虚拟机,是阿里云的ECS服务器,系统版本为CentOs7。

上传RocketMQ

RocketMQ的下载包:[密码6666](https://pan.baidu.com/s/1uBJyt88KuueBBgN-ZuxRMw)


将RocketMQ的zip包上传到阿里云服务器,使用命令解压。
1
2
shell复制代码unzip /usr/soft/rocketmq-all-4.4.0-bin-release.zip # 这个目录要改为自己的RocketMQ
mv /usr/soft/rocketmq-all-4.4.0-bin-release/ /usr/soft/rocketmq-4.4/ # 重命名方便管理

修改配置文件

进入RocketMQ的conf目录,修改`broker.conf`,新增三行(去掉我注释的中文)。
1
2
3
markdown复制代码enablePropertyFilter=true  # 支持标签顾过滤消息
namesrvAddr=127.0.0.1:9876 # 你的mqnamesrv地址
brokerIP1=11.11.11.11 # 你的阿里云服务器的公网ip

修改启动参数

因为默认的RocketMQ启动的时候需要的1JVM内存很多,会报错,所以我们将他变小一点即可。默认是1G,修改为512m。
1
2
shell复制代码vi  /usr/soft/rocketmq-4.4/bin/runbroker.sh
vi /usr/soft/rocketmq-4.4/bin/runserver.sh

image-20210511214735062

防火墙和安全组开放几个端口

开放9876、10911、10912、10909以及你指定的可视化工具启动的端口,这里步骤不再赘述,有疑问可以在评论区回复或者翻我的Linux的学习文章进行学习。

启动服务

1
2
shell复制代码nohup sh /usr/soft/rocketmq-4.4/bin/mqnamesrv &
nohup sh mqbroker -n localhost:9876 -c ../conf/broker.conf &

查看日志

在RocketMQ的根目录的bin目录中,有一个`nohup.out`用于存放启动的日志信息。无报错即成功,日志信息和1Windows的差不多。

2.5、RocketMQ的架构

image-20210511215533391

2.6、RocketMQ的核心概念

05_消息中间件核心概念

  • 名字服务Name Server(邮局):消息队列的协调者,Broker向它注册路由信息,同时Producer和Consumer向其获取路由信息,名称服务充当路由消息的提供者。生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但相互独立,没有信息交换。
  • 代理服务器Broker Server(邮递员):Broker是RocketMQ的核心,消息中转角色,负责存储消息、转发消息。代理服务器在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。
  • 生产者Producer(寄件人):负责生产消息,一般由业务系统负责生产消息。一个消息生产者会把业务应用系统里产生的消息发送到broker服务器。RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送。同步和异步方式均需要Broker返回确认信息,单向发送不需要。
  • 消费者Consumer(收件人):负责消费消息,一般是后台系统负责异步消费。一个消息消费者会从Broker服务器拉取消息、并将其提供给应用程序。从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费。
  • Topic(省份):用来区分不同类型的消息,表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。
  • 标签Tag:为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。
  • 消息队列MessageQueue(邮件):为了提高性能和吞吐量,引入了Message Queue,一个Topic可以设置一个或多个Message Queue,这样消息就可以并行往各个Message Queue发送消息,消费者也可以并行的从多个 Message Queue读取消息
  • 消息内容Message:息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。RocketMQ中每个消息拥有唯一的Message ID,且可以携带具有业务标识的Key。系统提供了通过Message ID和Key查询消息的功能。
  • 生产者组Producer Group:生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。
  • 消费者组Consumer Group:消费者组,消费同一类消息的多个 consumer 实例组成一个消费者组。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

java操作sql server数据库

发表于 2021-08-22

这是我参与8月更文挑战的第22天,活动详情查看: 8月更文挑战

请先确保已经设置好了sa,如果不是,可以参照下面链接修改jingyan.baidu.com/article/8cd…

然后重启数据库,重启完毕后,接下来使用命令测试1433端口是否打开。

接着开始菜单 → 运行cmd → 在命令提示符下输入:telnet 127.0.0.1 1433,(注意telnet与127之间有空格,1与1433之间有空格)若提示“不能打开到主机的连接,在端口 1433: 连接失败”,则说明1433端口没有打开,需要重新进行以上配置。如果是显示telnet不是内部命令,则按照图后步骤进行设置,若连接成功,显示如图8所示:

打开控制面板,进入程序,然后点击打开或者关闭windows,往下拉,可以看到telnet客户端,选择即可。然后重复上面第五的步骤。 jingyan.baidu.com/article/652…

配置环境变量classpath,将该sqljdbc4放置在jdk1.7(你们的版本)lib中,jingyan.baidu.com/article/cbc…

这是一条关于环境变量的设置链接。

右击电脑的属性,然后点击高级属性设置,点击环境变量设置,将该jar添加到之前安装的目录下,如果没有,则新建,并且为其设置地址目录。

三、使用eclipse测试链接SQL server 2008 数据库:

1、打开SQL server 2008 数据库,新建一个数据库jsp。

2、运行eclipse新建一个project,然后新建一个包,包里新建一个java文件,Main文件。

3、将sqljdbc4.jar copy 进入该project ,然后右击它,点击build path。(同时也可以把sqljdbc4jar类放到jre/lib/ext下,并放到tomcat/lib下,这样就可以应用到所有jsp文件,减少对每个项目进行sqljdbc4jar的加载)

4、进入Main.java,将下面的代码复制进入

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
30
java复制代码package pkg;

import java.sql.*;


public class Main {
public static void main(String[] args) {
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
String dbURL = "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=jsp";
String userName = "sa";
String userPwd = "123";

try {
Class.forName(driverName);
System.out.println("加载驱动成功!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("加载驱动失败!");
}

try {
Connection dbConn = DriverManager.getConnection(dbURL, userName,
userPwd);
System.out.println("连接数据库成功!");
} catch (Exception e) {
e.printStackTrace();
System.out.print("SQL Server连接失败!");
}
}
}

如果在连接过程中出现 talnet 127.0.0.1 post 1433 no response,先检查一遍是否设置有误,如果没有,那重新启动下计算机,因为可能未更新,我也是遇到这个问题,后来重启后便成功连接上了数据库。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

shiro笔记+Shiro 整合Springboot 实现

发表于 2021-08-22

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

shiro简介
什么是Shiro?
Apache Shiro 是一个Java 的安全(权限)框架
shiro 可以非常容易的开发出足够好的应用,其不仅可以用在Javase 环境,也可以用在Javaee环境
shiro 可以完成,认证,授权,加密,会话管理,Web 集成,缓存等。
在这里插入图片描述

  • ●Authentication:身份认证、登录,验证用户是不是拥有相应的身份;
  • ●Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操
  • 作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
  • ●Session Manager:会话管理,即用户登录后就是第一次会话, 在没有退出之前,它的所有信息都在会话中;
  • 会话可以是普通的JavaSE环境,也可以是Web环境;
  • ●Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
  • ●Web Support: Web支持, 可以非常容易的集成到Web环境;
  • ●Caching: 缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
  • ●Concurrency: Shiro支持多线程应用的并发验证,即,如在一 个线程中开启另一 一个线程,能把权限自动的传
  • 播过去
  • ●Testing: 提供测试支持;
  • ●Run As:允许-一个用户假装为另-一个用户(如果他们允许)的身份进行访问;
  • ●Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
    在这里插入图片描述
  • ●Subject: 任何可以与应用交互的’用户’;
  • ●Security Manager:相当于SpringMVC中的DispatcherServlet; 是Shiro的心脏,所有具体的交互都通过
  • Security Manager进行控制,它管理者所有的Subject, 且负责进行认证,授权,会话,及缓存的管理。
  • ●Authenticator: 负责Subject认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication
  • Strategy),即什么情况下算用户认证通过了;
  • ●Authorizer: 授权器,即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中
  • 的那些功能;
  • ●Realm:可以有一个或者多个的realm, 可以认为是安全实体数据源,即用于获取安全实体的,可以用DBC实
  • 现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm
  • ●SessionManager: 管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的
  • JavaSE环境中
  • ●Authorizer: 授权器,即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中
  • 的那些功能; .
  • ●Realm: 可以有一一个或者多个的realm,可以认为是安全实体数据源,即用于获取安全实体的,可以用DBC实
  • 现,也可以是内存实现等等,由用户提供;所以-般在应用中都需要实现自己的realm
  • ●SessionManager: 管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的
  • JavaSE环境中
  • CH用英J”,简筵:
  • ●CacheManager: 缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓
  • 存中后可以提高访问的性能;
  • ●Cryptography: 密码模块,Shiro提高了- -些常见的加密组件用于密码加密,解密等
  • 快速开始的第一个shiro*
  • pom .xml*
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
xml复制代码<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>shiro</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>

<!-- https://mvnrepository. com/artifact/org. apache. shiro/shiro-core -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.4.1</version>

</dependency>

<!-- configure logging -->

<dependency>

<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>

</dependency>
<!--日志 start-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

</dependencies>


</project>

log4j.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

shiro.ini

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
java复制代码#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# =============================================================================
# Quickstart INI Realm configuration
#
# For those that might not understand the references in this file, the
# definitions are all based on the classic Mel Brooks' film "Spaceballs". ;)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

官网复制就行哈
第一个demo 类

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
java复制代码/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
//import org.apache.shiro.ini.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
//import org.apache.shiro.lang.util.Factory;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {

private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


public static void main(String[] args) {

//官方写的的代码 都过时了,哈哈哈哈哈
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();


SecurityUtils.setSecurityManager(securityManager);



//获取当前执行的用户:
Subject currentUser = SecurityUtils.getSubject();

//使用Session做一些事情(不需要Web或EJB容器!!!)
Session session = currentUser.getSession();
// 设置session 会话
session.setAttribute("someKey", "aValue");
// 拿到对应的值
String value = (String) session.getAttribute("someKey");
// 判断
if (value.equals("aValue")) {
// 打印查看一下
System.out.println("value = " + value);
log.info("Retrieved the correct value! [" + value + "]");
}
//············································································以上是Subject 的工作
//让我们登录当前用户,以便我们可以检查角色和权限:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ...在这里捕获更多异常(也许是针对您的应用程序的自定义异常?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//说明他们是谁:
//打印其标识主体(在本例中为用户名):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}

//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}

//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}

//all done - log out!
currentUser.logout();

System.exit(0);
}
}

结果

1
2
3
4
5
6
7
8
9
10
java复制代码"C:\Program Files\Java\jdk1.8.0_231\bin\java.exe" "-javaagent:D:\idea\IntelliJ IDEA 2019.3.5\lib\idea_rt.jar=62162:D:\idea\IntelliJ IDEA 2019.3.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_231\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\rt.jar;E:\shiro\target\classes;E:\maven_jar包\org\apache\shiro\shiro-core\1.4.1\shiro-core-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-lang\1.4.1\shiro-lang-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-cache\1.4.1\shiro-cache-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-crypto-hash\1.4.1\shiro-crypto-hash-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-crypto-core\1.4.1\shiro-crypto-core-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-crypto-cipher\1.4.1\shiro-crypto-cipher-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-config-core\1.4.1\shiro-config-core-1.4.1.jar;E:\maven_jar包\org\apache\shiro\shiro-config-ogdl\1.4.1\shiro-config-ogdl-1.4.1.jar;E:\maven_jar包\commons-beanutils\commons-beanutils\1.9.3\commons-beanutils-1.9.3.jar;E:\maven_jar包\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;E:\maven_jar包\org\apache\shiro\shiro-event\1.4.1\shiro-event-1.4.1.jar;E:\maven_jar包\org\slf4j\jcl-over-slf4j\1.7.21\jcl-over-slf4j-1.7.21.jar;E:\maven_jar包\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;E:\maven_jar包\org\slf4j\slf4j-log4j12\1.7.21\slf4j-log4j12-1.7.21.jar;E:\maven_jar包\log4j\log4j\1.2.17\log4j-1.2.17.jar" Quickstart
2020-12-23 20:09:50,625 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
value = aValue
2020-12-23 20:09:51,315 INFO [Quickstart] - Retrieved the correct value! [aValue]
2020-12-23 20:09:51,315 INFO [Quickstart] - User [lonestarr] logged in successfully.
2020-12-23 20:09:51,315 INFO [Quickstart] - May the Schwartz be with you!
2020-12-23 20:09:51,315 INFO [Quickstart] - You may use a lightsaber ring. Use it wisely.
2020-12-23 20:09:51,315 INFO [Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. Here are the keys - have fun!

Process finished with exit code 0

分析一下源码
在这里插入图片描述
在这里插入图片描述
在那个inl 里解释了角色的指定
在这里插入图片描述
在这里插入图片描述
Springboot 跟Shiro 环境搭建
1,导入依赖

1
2
3
4
5
6
7
8
9
10
11
xml复制代码        <!--引入thymeleaf依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- shiro-spring 依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>

写config 类
在这里插入图片描述
Shior Config 类

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
30
31
32
33
34
35
java复制代码package com.jj.demo.config;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
// shiro 的三个
// Subject 用户
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;

}
// SecurityManager 管理所有的用户
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 关联 Realm
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}

// Realm 连接数据
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}

Shiro 主要的三个
// Subject 用户
// SecurityManager 管理所有的用户

1
arduino复制代码// Realm 连接数据

写自己的Realm 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码package com.jj.demo.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class UserRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("principalCollection = 授权" + principalCollection);
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("authenticationToken = 认证" + authenticationToken);
return null;
}
}

简单的几个页面和控制层
在这里插入图片描述
控制层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码package com.jj.demo.control;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class Shirocontrol {
@RequestMapping({"/","/index"})
public String demo(Model model){
model.addAttribute("msg","你好啊,我的第一个Springboot-sgiro 程序!!");
return "index";
}
//跳转到添加
@RequestMapping("/user/add")
public String add(){
return "/user/add";
}
//跳转到更新
@RequestMapping("/user/update")
public String update(){
return "/user/update";
}
}

在这里插入图片描述
实现登录拦截功能!!
在这里插入图片描述
简单的写个登录的控制层,还有登录页面就可以了
效果
在这里插入图片描述
在这里插入图片描述
走到了我们指定的url
控制层写一个登录的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码//    登录的
@RequestMapping("login")
public String login1(String name,String pwd,Model model){
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装当前登录的数据 获取到令牌
UsernamePasswordToken token = new UsernamePasswordToken(name,pwd);
try {
subject.login(token); //执行登录的方法,没有异常就跳出
return "index";
//
// 用户名不存在异常
} catch (UnknownAccountException uae) {
model.addAttribute("msg","用户名错!!");
return "login";
}

catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "login";

}
}

会自动与我们写的UserRealm 的认证联系起来。效果

1
2
3
4
5
java复制代码2020-12-24 16:09:05.446  INFO 4028 --- [           main] com.jj.demo.DemoApplication              : Started DemoApplication in 1.929 seconds (JVM running for 3.107)
2020-12-24 16:09:16.455 INFO 4028 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-12-24 16:09:16.456 INFO 4028 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-12-24 16:09:16.458 INFO 4028 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
authenticationToken = 认证org.apache.shiro.authc.UsernamePasswordToken - 娇娇, rememberMe=false

连接数据库测试
实体类简单的几个name,pwd
在这里插入图片描述
简单的根据name 做个查询 具体代码非常简单。在UserRealm 上注入service 层即可!!

在自己写的UserRealm 类里认证里写上
pom.xml

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
30
31
32
33
34
35
xml复制代码  <!--Lombok引入-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- shiro-spring 依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- log4-->
<!--日志 start-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!-- mysql 的-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis 的包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>

yml 配置

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
yaml复制代码# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/op?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
druid:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
#mybatis的配置
mybatis:
configuration:
# sql日志显示,这里使用标准显示
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 整合别名的包
# 数据库中如果有类似 如 user_name 等命名,会将 _后的字母大写,这里是为了和实体类对应
# map-underscore-to-camel-case: true
# 配置mapper文件的路径
type-aliases-package: com.jj.demo.pojo
mapper-locations: classpath:mapper/*.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("authenticationToken = 认证" + authenticationToken);

UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
vip vip = vipdaoserviceimpl.showbyname(userToken.getUsername());
if (vip.getName()==null){
return null;
//抛出异常
}
// 密码认证,shiro 来做

return new SimpleAuthenticationInfo("",vip.getPwd(),"");
}

授权功能!!
数据库添加字段
在这里插入图片描述
在 ShiroConfig 里加上如下代码
在这里插入图片描述
UserRealm类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码//    授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("principalCollection = 授权" + principalCollection);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加方法
info.addStringPermission("vip:add");
info.addStringPermission("vip:update");
// 拿到当前登录的对象
Subject subject = SecurityUtils.getSubject();
// 拿到vip 对象
vip vip = (com.jj.demo.pojo.vip) subject.getPrincipal();
// 设置当前用户的权限
System.out.println("vip.getPerms() = " + vip.getPerms());
info.addStringPermission(vip.getPerms());
return info;
}

在这里插入图片描述

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Logbackxml 详细配置

发表于 2021-08-22

Logback.xml 详细配置

项目中日志是必不可少的,有了日志方便定位问题,解决问题,SpringBoot项目推荐使用log4j或者logback。

一般logback用的稍微多一点,而且也更灵活。

很多时候配置logback,我都是从其他地方找一个xml,然后改一改,随着时间越来越久,很多配置都忘得差不多了,今天就好好总结一下。

logback-spring.xml

首先第一行是

<?xml version="1.0" encoding="UTF-8"?>

这个很简单,声明xml文件和编码格式。

接下来进入正题。

configuration

logback配置都包在一个 <configuration> 标签里。

<configuration scan='true' scanPeriod="60 seconds" debug="false">

  • scan:为true,配置文件发生改变,将会重新加载,默认为true
  • scanPeriod:监测配置文件是否有修改的时间间隔,默认单位是毫秒
  • debug:打印出logback内部日志,默认false

这里面可以声明的有:

  • springProperty
  • property
  • conversionRule
  • appender
  • root
  • logger
  • springProfile

springProperty

可以从application.properties配置文件中,获取属性。

比如这样,

<springProperty scope="context" name="LOG_PATH" source="logging.path"/>

  • scope:作用域
  • name:名称,在下面引用的时候的名字
  • source:在application.properties文件中的key
  • defaultValue:默认值,该属性在application.properties中未设置时,会使用默认值

property

相当于声明变量,指定name和value,在下面可以用${name}引用value

<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%p] [%.10t] [%c{1}][%L] %X{traceId} %m%n" />

conversionRule

主要用来配置转换器,可以自定义Pattern模板,也可以引入一些其他模板来解析Pattern,比如打印彩色日志,打印IP地址。

打印彩色日志转换器。
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />

  • conversionWord:在Pattern中的关键字
  • converterClass:转换器类

<property name="COLOR_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%p) %clr(${PID}){magenta} %clr([%.10t]){yellow} %clr([%c{1}]){blue}%clr([%L]){faint}%X{traceId} %m%n"/>

上面%clr就是要用ColorConverter来解析,后面{blue}就是要展示的颜色。

彩色效果:

Pattern表达式

  • %d:输出日志的打印时间,模式语法与java.text.SimpleDateFormat 兼容(d/date)
  • %p:输出日志级别(p / le / level)
  • ${PID}:输出进程Id
  • %.10t:输出产生日志的线程名。(t / thread)
  • %c{1}:输出日志的logger名 (c / lo / logger)
  • %L:输出执行日志请求的行号 (L / line)
  • %X:和当前线程相关联的NDC(嵌套诊断环境) ,追踪Id,MDC用
  • %m:输出应用程序提供的信息,详细日志 (m / msg / message)
  • %n:换行

还有一些其他的不常用的:

  • C / class:输出执行记录请求的调用者的全限定名,和c一样,尽量避免使用 例如:%C{5}
  • contextName / cn:输出上下文名称。
  • F / file:输出执行记录请求的java源文件名。尽量避免使用。
  • caller{depth}:输出生成日志的调用者的位置信息,整数选项表示输出信息深度。例如:%caller{2}
  • M / method:输出执行日志请求的方法名。尽量避免使用。
  • r / relative:输出从程序启动到创建日志记录的时间,单位是毫秒。
  • replace(p ){r, t}:p 为日志内容,r 是正则表达式,将p 中符合r 的内容替换为t 。例如: “%replace(%msg){‘\s’, ‘’}”

配置caller后

格式符

  • 左对齐修饰符(-):接着是可选的最小宽度修饰符,用十进制数表示。如果字符小于最小宽度,则左填充或右填充,默认是左填充(即右对齐),填充符为空格。如果字符大于最小宽度,字符永远不会被截断。
  • 最大宽度修饰符(.):后面加十进制数。如果字符大于最大宽度,则从前面截断。后面加减号“-”在加数字,表示从尾部截断。

appender

写日志的组件,有两个必要属性name和class。name指定appender名称,可以自定义,class指定appender的全限定名。

控制台打印:

1
2
3
4
5
6
xml复制代码<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${COLOR_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
  • pattern:设置打印的格式
  • charset:编码格式

打印到文件

1
2
3
4
5
6
7
8
9
10
11
xml复制代码<appender name="xxxx" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/xxxx.log</file>
<encoder>
<pattern>${LOG_PATTERN1}</pattern>
<charset>utf-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/xxxx.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
  • ch.qos.logback.core.rolling.RollingFileAppender:滚动记录日志
  • file:写入的日志文件路径、名称,如果文件不存在,则新建
  • encoder:设置编码
    • pattern:日志打印的格式
    • charset:编码格式
  • rollingPolicy:ch.qos.logback.core.rolling.TimeBasedRollingPolicy 设置滚动策略,按照时间来保存
  • fileNamePattern:日志名称格式,如果以gz结尾,会自动压缩。
  • maxHistory:日志保存的最大天数,这个天数之前的日志会被自动删除。

appender中还可以配置ch.qos.logback.classic.net.SMTPAppender,在发生异常时,可以发送邮件,
还有ch.qos.logback.classic.db.DBAppender,可以把日志保存到数据库。

root

1
2
3
4
xml复制代码<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="xxxx"/>
</root>
  • root:是所有logger的上级,可以设置日志输出级别。默认是DEBUG。其实它也是一个logger,但是只有level属性。
  • appender-ref:日志被打印到控制台和日志文件中。

logger

1
2
3
4
5
xml复制代码<logger name="com.ler.yshi.interceptor.PrintSqlInterceptor" level="logger_dao_level" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="xxxx"/>
<appender-ref ref="sql"/>
</logger>
  • logger:用来设置某一个包或者具体的某一个类的日志打印级别,
  • name:用来指定受此logger约束的某一个包或者具体的某一个类,com.ler.yshi.interceptor.PrintSqlInterceptor这个类是我打印完整SQL的拦截器。
  • level:日志级别
  • addtivity:是否向上级logger传递打印信息。默认是true
  • appender-ref:
    • logger可以包含零个或多个元素,标识这个appender将会添加到这个logger。这里的意思就是PrintSqlInterceptor的SQL日志会被打印到控制台,日志文件,以及SQL日志文件中

springProfile

在logback.xml中还可以使用springProfile来为多环境配置不同的日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
xml复制代码<!-- 开发环境日志级别为DEBUG -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
</root>
</springProfile>

<!-- 测试环境日志级别为INFO -->
<springProfile name="test">
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
</root>
</springProfile>

不过这个用的不是很多。

logback知道这些,基本上使用是没什么问题了,还有一些复杂的,其实也没必要搞那么复杂。

好了,最后欢迎大家关注我的公众号,共同学习,一起进步。加油🤣

南诏Blog

参考

blog.csdn.net/snail_bi/ar…

www.cnblogs.com/cb0327/p/57…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Cloud Eureka Client 端服务发现

发表于 2021-08-21

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

文章目的 :

  • 深入 Client 端服务发现的流程

概念补充
region:可以简单理解为地理上的分区,比如上海地区,或者广州地区,再或者北京等等,没有具体大小的限制。根据项目具体的情况,可以自行合理划分region。

zone:可以简单理解为region内的具体机房,比如说region划分为北京,然后北京有两个机房,就可以在此region之下划分出zone1,zone2两个zone。

二 . 调用的起点

2.1 初始化

Eureka Client 在初始化 EurekaClient 类的同时发起了服务发现的逻辑 , 主要调用流程为 :

  • Step 1 : C- EurekaClientAutoConfiguration

    • PVC- RefreshableEurekaClientConfiguration 中进行 EurekaClient 的 Bean 加载
  • Step 2 : 调用 DiscoveryClient 构造器

  • Step 3 : Client 发起 FullRegistry 操作

Step 1 : Configuration 的 Bean 加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config, EurekaInstanceConfig instance,
@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
ApplicationInfoManager appManager;
if (AopUtils.isAopProxy(manager)) {
appManager = ProxyUtils.getTargetObject(manager);
}
else {
appManager = manager;
}
// 构建 CloudEurekaClient 对象
CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,config, this.optionalArgs, this.context);
// 注册健康检查对象
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
return cloudEurekaClient;
}

// PS : 调用 CloudEurekaClient 构造器中会调用其父类 DiscoveryClient 的构造函数

Step 2 : DiscoveryClient full Registry

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
30
31
32
33
34
35
java复制代码private boolean fetchRegistry(boolean forceFullRegistryFetch) {
Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

try {
// If the delta is disabled or if it is the first time, get all
// applications
Applications applications = getApplications();

if (clientConfig.shouldDisableDelta()
|| (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
|| forceFullRegistryFetch
|| (applications == null)
|| (applications.getRegisteredApplications().size() == 0)
|| (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
{
// 从eureka服务器获取完整的注册表信息并将其存储在本地
getAndStoreFullRegistry();
} else {
getAndUpdateDelta(applications);
}
applications.setAppsHashCode(applications.getReconcileHashCode());
logTotalInstances();
} catch (Throwable e) {

}

// 在更新实例远程状态之前通知缓存刷新
onCacheRefreshed();

// 基于缓存中保存的刷新数据更新远程状态
updateInstanceRemoteStatus();

// 成功获取注册表,因此返回true
return true;
}

Step 3 : 查询 Registry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码private void getAndStoreFullRegistry() throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();

Applications apps = null;

// 通过 Client 获取 Applications
EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
: eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());

if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
apps = httpResponse.getEntity();
}


if (apps == null) {
//............
} else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
// 重点 : 设置到 AtomicReference<Applications> 对象中
localRegionApps.set(this.filterAndShuffle(apps));
} else {
}
}

三 . 服务的发现

从上面看到 , 通过 Client 搜索了 Registy 对象 , 后面看一下具体的查询流程

3.1 service 的获取

Step 1 : EurekaDiscoveryClient 发起请求

1
2
3
4
java复制代码// EurekaDiscoveryClient 中通过如下代码获取 :
Applications applications = this.eurekaClient.getApplications();

// PS : 此处的 eurekaClient 为 CloudEurekaClient

Step 2 : 获取 Applications

1
2
3
4
5
6
java复制代码// 这里是通过一个原之类缓存了所有的 Applications
private final AtomicReference<Applications> localRegionApps = new AtomicReference<Applications>();

public Applications getApplications() {
return localRegionApps.get();
}

PS : Applications 数据如下
image.png

Step 3 : Applications 的缓存

上文知道了获取的时机 , 这里再来看一下数据是在什么时候缓存进去的 :

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
java复制代码// 节点一 : 获取 Client Registry
private void getAndStoreFullRegistry() throws Throwable {
// 上文看了 发起的流程 , 调用了一个 Client 发起
}


// 节点二 : 远程获取 Application
public EurekaHttpResponse<Applications> getApplications(final String... regions) {
return execute(new RequestExecutor<Applications>() {
@Override
public EurekaHttpResponse<Applications> execute(EurekaHttpClient delegate) {
return delegate.getApplications(regions);
}

@Override
public RequestType getRequestType() {
return RequestType.GetApplications;
}
});
}


// 节点三 : 循环调用不同的 HttpClient , 如下图总共提供了四种 HttpClient , 此处主要为 SessionedEurekaHttpClient
protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) {
long now = System.currentTimeMillis();
long delay = now - lastReconnectTimeStamp;
if (delay >= currentSessionDurationMs) {
logger.debug("Ending a session and starting anew");
lastReconnectTimeStamp = now;
currentSessionDurationMs = randomizeSessionDuration(sessionDurationMs);
TransportUtils.shutdown(eurekaHttpClientRef.getAndSet(null));
}

EurekaHttpClient eurekaHttpClient = eurekaHttpClientRef.get();
if (eurekaHttpClient == null) {
eurekaHttpClient = TransportUtils.getOrSetAnotherClient(eurekaHttpClientRef, clientFactory.newClient());
}

// 回调节点二传入的匿名对象
return requestExecutor.execute(eurekaHttpClient);
}

// 节点四 : Internet 调用 , 可以看到调用的 app 接口 C- AbstractJerseyEurekaHttpClient
public EurekaHttpResponse<Applications> getApplications(String... regions) {
return getApplicationsInternal("apps/", regions);
}

// 没想到啊没想到 , 这里居然看到了 jerseyClient , 这玩意这么底层的吗 C-
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) {
ClientResponse response = null;
String regionsParamValue = null;
try {
WebResource webResource = jerseyClient.resource(serviceUrl).path(urlPath);
if (regions != null && regions.length > 0) {
regionsParamValue = StringUtil.join(regions);
webResource = webResource.queryParam("regions", regionsParamValue);
}
Builder requestBuilder = webResource.getRequestBuilder();
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);

Applications applications = null;
if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) {
applications = response.getEntity(Applications.class);
}
return anEurekaHttpResponse(response.getStatus(), Applications.class)
.headers(headersOf(response))
.entity(applications)
.build();
} finally {

if (response != null) {
response.close();
}
}
}

补充 : Eureka HttpClient 体系

Eureka-Sytsem-EurekaHttpClientDecorator.png

补充 : 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
java复制代码
[
[{
"host": "localhost",
"port": 8088,
"secure": false,
"uri": "http://localhost:8088",
"metadata": {
"management.port": "8088"
},
"instanceId": "DESKTOP-TSNPTAK:GANG-EUREKA:8088",
"serviceId": "GANG-EUREKA",
"instanceInfo": {
"instanceId": "DESKTOP-TSNPTAK:GANG-EUREKA:8088",
"app": "GANG-EUREKA",
"appGroupName": null,
"ipAddr": "192.168.0.6",
"sid": "na",
"homePageUrl": "http://localhost:8088/",
"statusPageUrl": "http://localhost:8088/actuator/info",
"healthCheckUrl": "http://localhost:8088/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "GANG-EUREKA",
"secureVipAddress": "GANG-EUREKA",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "localhost",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1628746104090,
"lastRenewalTimestamp": 1628748325836,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1628746074745
},
"isCoordinatingDiscoveryServer": true,
"metadata": {
"management.port": "8088"
},
"lastUpdatedTimestamp": 1628746104090,
"lastDirtyTimestamp": 1628746074064,
"actionType": "ADDED",
"asgName": null
},
"scheme": null
}]
]

四 . 请求的使用

请求的使用可以参考 Feign 负载均衡 , 其本身也是从容器中获取 Server 列表

1
2
3
4
5
6
7
8
9
10
11
java复制代码public Object getServicesList() {
List<List<ServiceInstance>> servicesList = new ArrayList<>();
//获取服务名称
List<String> serviceNames = discoveryClient.getServices();
for (String serviceName : serviceNames) {
//获取服务中的实例列表
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
servicesList.add(serviceInstances);
}
return servicesList;
}

Step : Applicaiton 的存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码public List<String> getServices() {
LinkedHashSet<String> services = new LinkedHashSet<>();
if (this.discoveryClients != null) {
// 可以看到 , 这里从多个 discoveryClients 中获取 Services
for (DiscoveryClient discoveryClient : this.discoveryClients) {
List<String> serviceForClient = discoveryClient.getServices();
if (serviceForClient != null) {
services.addAll(serviceForClient);
}
}
}
return new ArrayList<>(services);
}

C- DiscoveryClient : 该对象在 2.1 Step 2 中设置了
AtomicReference<Applications> localRegionApps = new AtomicReference<Applications>();

五 . 总结

核心对象就是一个 AtomicReference ,通过 AbstractJerseyEurekaHttpClient 调用 /app 查询所有的 Server

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

高频面试题-请聊一下Springboot自动装配原理!

发表于 2021-08-21

这是我参与8月更文挑战的第2天,活动详情查看: 8月更文挑战

自从SpringBoot的出现,让Java的基本系统搭建过程变得更简单,不需要关心很多的配置文件,也不需要去关心市面上各版本的包存在版本冲突等等的问题,使得SpringBoot让开发人员推崇备至。

SpringBoot

在面试过程中,SpringBoot的面试题也同样是非常频繁的,简单点的有SpringBoot有哪些核心注解?、SpringBoot如何启动?、SpringBoot中yml配置文件有什么不同之处?等等吧,问这些都是在检测你是否对SpringBoot有了解。

当然还有一些细节点的面试题有SpringBoot如何实现热部署?、SpringBoot如何设置服务端口号?、SpringBoot如何做到切换不同环境的配置文件?等等,这些问题就不同了,如果面试官问出了这些,许是在检测你是否真正的使用过SpringBoot,或者是否对SpringBoot项目有一定的基础。

除了以上面试题目之外,还会有些问题,用于检测你是否对SpringBoot有更深入的研究,不再局限于使用层面,比如:SpringBoot如何实现的自动装配?、SpringBoot中starter的原理?等等。

今天我们就是来说一下SpringBoot中的自动装配原理,自动装配是SpringBoot中的一个非常重要的,也是非常具有特色的功能,下面就来看一下吧。

自动装配

从启动类开始看

说起SpringBoot中的自动装配,首先要从SpringBoot的启动类开始看。

1
2
3
4
5
6
7
8
java复制代码@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

这是一个基本的SpringBoot启动类,我们主要看SpringBootApplication这个注解,点进去源码,看一下具体的实现。

image.png

根据图中的信息,我们完全可以看得出来,SpringBootApplication这个注解,是一个复合注解。

SpringBootConfiguration注解,可能会有些陌生,但是对于@Configuration注解的话,就一定不会了;
ComponentScan也是我们在Spring项目中常常会用到的扫描注解。

主要还是来说一下@EnableAutoConfiguration注解,自动配置注解,也可以说自动装配,既然是要聊自动装配原理,那也就是聊一下@EnableAutoConfiguration注解的具体实现了,下面就来看一下吧。

开启自动配置注解@EnableAutoConfiguration

看一下此注解的源码实现,如下图。

image.png

在这个注解实现中,从AutoConfiguration注解开始看起。

简单说下在此之上的注解,如果你写过自定义注解的话,就能很好的分辨这几种的概念了。

1
2
3
4
5
6
7
less复制代码@Target({ElementType.TYPE}) : 用于声明注解作用范围,TYPE表示:可用来修饰接口、类、枚举、注解;还有其他的枚举类型,如果有兴趣可以去自行搜索一下。

@Retention(RetentionPolicy.RUNTIME) :用于声明注解的生命周期,RUNTIME表示:在运行时有效。

@Documented :用于声明注解文档化,如果被此注解修饰,就可以用javadoc进行记录,成为了一个公共的API。

@Inherited :用于加强继承效果。

回到正题,接着说。

AutoConfiguration注解同样不是实现自动装配的注解,在Spring中,此注解用来扫描实体类Entity等注解。

Import注解才是自动装配的核心,继续深入。

Import注解实现了AutoConfigurationImportSelector类,自动装配也是在这个类中进行了具体的实现。

AutoConfigurationImportSelector类中实现了诸多方法,自动装配的实现则是在selectImports方法中,如图所示。

image.png

从源码中读起来,有一个getCandidateConfigurations方法,进入看一下代码情况。

image.png

这里存在一个断言,意为无法正确的找到spring.factories文件,结果就很自然了,这个方法就是去加载了spring.factories文件,让我们去找一下这个文件里面具体是什么内容吧。

通过IDEA中的当前类定位按钮进行寻找,

image.png

在这里能找到文件,如图:

image.png

文件内容如图:

image.png

可以看出,通过selectImports方法,取到该文件下的一系列类名,随后将这些类自动加载至IOC容器中。

这些类都属于内部存在自动配置的类,同样可以发现这些类名都是以AutoConfiguration结尾的。

总结

自动装配原理就已经说完了,总结一下,就是通过@EnableAutoConfiguration注解,加载AutoConfigurationImportSelector类中的selectImports方法,进而扫描MATE-INF下的spring.factories文件下的自动配置类,并将其装配至IOC容器的过程。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

高并发下账户余额扣减解决方案

发表于 2021-08-21

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

分类

高并发情况下,一般采用乐观锁的方式来保证账户余额的正确性。账户的竞争对象及处理方式见下表:

分类 处理方式
用户与用户 快速失败,提醒稍后再试
系统与系统 多次尝试,保存可重试信息
系统与用户 多次尝试,保存现场

设计

账户表

1
2
3
4
5
6
7
8
9
10
11
12
kotlin复制代码public class Account {

@Id
private Long id;

@Column(nullable = false)
private Bigdecimal Money;

// 乐观锁控制
@Version
private int version;
}

记录表

1
2
3
4
5
6
7
8
9
10
11
12
13
kotlin复制代码public class AccountRecord {

@Id
private Long id;

private Long accountId;

@Column(nullable = false)
private Bigdecimal payMoney;

@Column(nullable = false)
private Bigdecimal incomeMoney;
}

注意:记录表中是否要增加余额字段,要根据情况来定,如果记录表不允许用户删除和修改,建议加上,方便核账及排查,反之不要加。

用户与用户竞争–多端操作

当用户在多个终端同时进行提现时前端校验是失效的,就有可能发生锁竞争,但这种情况属于用户的主动操作,所以我们可以在发生乐观锁时直接返回错误信息,警告用户勿频繁操作,流程图如下:

未命名文件.png

系统与系统竞争

充值成功后,三方返回异步通知,系统进行账户操作,此时也刚好系统在发放红包,就有可能发生锁竞争,因为都是系统内的操作,所以我们一定要保护好现场,并在发生乐观锁异常时,一方要有重试机制,重试达到设定值后,要保存所必须的信息,以便后续人工干预并重新执行任务。流程图如下:

未命名文件 (1).png

最后

总结一下,系统内部的,切记要有重试机制并可再次执行,用户类的主动操作就不用考虑那么多,直接返回错误信息就好。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

开发人员,怎能没有个人博客!前人所创经典ZBlog

发表于 2021-08-21

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑

初始 ZBlog

  Z-Blog官方网站,开源免费、小巧强大的博客程序与CMS建站系。Z-Blog和Z-BlogPHP,既是博客程序,也是CMS建站系统。已走过十余年风雨的她们,有着强大的可定制性、丰富的插件接口和独立的主题模板,致力于给国内用户提供优秀的博客写作体验。

ZBlog特点

  • 支持 PHP 5.2 - 7.4、 8.0
  • 支持 MySQL、PostgreSQL、SQLite 数据库
  • 一键下载应用中心主题与插件
  • 支持在线安装,在线更新与校验
  • 用户中心
  • 统一Z-Blog网站所有账号系统
  • 支持GitHub和QQ登录
  • 支持手机绑定
  • 开放了两步验证和单次Token系统
  • 支持Access+MSSQL双数据库
  • 可实现静态化、伪静态、动态页面
  • 全新的默认主题和后台界面UI
  • 精心打造全新的应用中心

快速开始

一键安装

1、选择宝塔一键部署源码

图片.png

2、选择Z-Blog原码

图片.png

3、点击一键部署

图片.png

4、依次输入域名、备注、根目录、数据库、选择源码,默认源码即可,选择PHP版本,一般版本默认即可。

图片.png

5、点击创建就开始部署源码啦

图片.png

6、已成功部署Z-Blog,直接进行访问即可。

图片.png

7、在浏览器输入刚刚的域名,点击访问即可,出现如下页面即搭建成功。是不是很简单呢。

图片.png

管理控制

1、点击登录后台

图片.png

2、输入用户名密码
3、修改网站名称,在后台点击网站设置,修改网站标题即可

图片.png

4、修改完成,在网站首页展示位小阿杰的博客

图片.png

5、在网站后台左边,可以新建文章,管理目录有:文章管理、页面管理、分类管理、标签管理、评论管理、附件管理用户管理和模块管理等。可以对文章相关信息进行管理。

图片.png

6、发表第一篇文章,点击新建文章,输入文章标题和文章正文,

图片.png

7、选择分类、状态、标签和发布时间、作者即可。如下图,点击发布。

图片.png

8、在网站首页即出现了刚刚发布的文章,是不是很简单呢,赶快建立自己的博客发布吧。

图片.png

结语

  如果大家有个人技术网站或者博客,也可以一起交流学习,加个友情链接。作者介绍:【小阿杰】一个爱鼓捣的程序猿,JAVA开发者和爱好者。公众号【Java全栈架构师】维护者,欢迎关注阅读交流。

  好了,感谢您的阅读,希望您喜欢,如对您有帮助,欢迎点赞收藏。如有不足之处,欢迎评论指正。下次见。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

只需三步完成Linux中的Python升级 1下载安装包

发表于 2021-08-21

因个人需要,本人要将一个python文件上传到云服务器运行,服务器安装的Linux镜像为CentOS 7.2,在使用过程中发现系统默认安装的python版本是python2,该版本不仅官方已经停止更新,而且也不符合大多数用户的选择,例如我上传的文件就是采用python3编写的,为了防止后续再次出现python版本不兼容的情况,我这里直接将其版本升级到python 3.9.5,具体的版本选择请各自斟酌,不必追求高版本。升级步骤如下:

1.下载安装包

首先我们新建一个目录,用于保存下载的python安装包,然后将其解压。

1
2
3
4
5
csharp复制代码[root@lg10 ~]# mkdir -p /opt/python3
[root@lg10 ~]# cd /opt/python3/
[root@lg10 python3]# wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tar.xz
[root@lg10 python3]# tar -xvf Python-3.9.5.tar.xz
[root@lg10 python3]# cd Python-3.9.5/

这里我们直接是在python的官方网站下载的,因此速度有点慢,如果国内有合适的镜像,建议把下载链接改成国内的,这样会快很多。

2.编译安装

1
2
csharp复制代码[root@lg10 Python-3.9.5]# ./configure --prefix=/usr/local/python3
[root@lg10 Python-3.9.5]# make && make install

--prefix参数指定了软件的安装目录,该软件所有的安装文件都被复制到这个目录,其好处是方便我们卸载或移植软件,推荐使用。

在安装过程中,我遇到了如下的问题:

zipimport.ZipImportError: can’t decompress data

问题的原因是缺少zlib的相关工具包,我们只需要安装相关依赖包即可

1
复制代码yum -y install zlib*

然后重新执行编译就成功了。

3.新建软连接

软连接是Linux中的一个常用命令,它的功能是为某一个文件在另外一个位置建立一个链接。软链接文件类似于Windows的快捷方式,它实际上是一个特殊的文件,其中保存的是另一个文件的位置信息。

由于系统已经安装了python2的版本,因此这里在bin下的启动程序还是python2的,这里我们需要把老版本做个移置更名作为备份,否则直接命令行执行python,打开的还是老版本。

1
csharp复制代码[root@lg10 Python-3.9.5]# mv /usr/bin/python /usr/bin/python2

然后我们需要新建一个软连接到/usr/bin下,这样默认直接打开python就会指向新安装的python3了。

1
csharp复制代码[root@lg10 Python-3.9.5]# ln -sv /usr/local/python3/bin/python3.9.5 /usr/bin/python

现在,如果我们直接输入python,默认打开的就是python3。

1
2
3
4
csharp复制代码[root@lg10 ~]# python
Python 3.9.5 (default, Aug 21 2021, 16:39:54)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.

这里我们并没有删除python2,而是留在这里作为备用,如果输入python2就可以切换到python2。

1
2
3
4
csharp复制代码[root@lg10 ~]# python2
Python 2.7.5 (default, Aug 7 2019, 00:51:29)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…555556557…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%