首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10014篇文章
优秀的个人博客,低调大师

Jenkins环境搭建总结

Jenkins是一个用Java编写的开源的持续集成(CI)工具,可持续、自动地构建/测试软件项目,监控一些定时执行的任务。具有开源,支持多平台和插件扩展,安装简单,界面化管理等特点。 一、Jenkins安装 1.安装OpenJDK [root@localhost~]#yuminstalljava-1.7.0-openjdk [root@localhost~]#java-version javaversion"1.7.0_131" OpenJDKRuntimeEnvironment(rhel-2.6.9.0.el6_8-x86_64u131-b00) OpenJDK64-BitServerVM(build24.131-b00,mixedmode) 2.yum安装Jenkins stable版本 [root@localhost~]#wget-O/etc/yum.repos.d/jenkins.repohttp://pkg.jenkins-ci.org/redhat-stable/jenkins.repo [root@localhost~]#rpm--importhttps://jenkins-ci.org/redhat/jenkins-ci.org.key [root@localhost~]#yuminstalljenkins 安装好后,jenkins的主目录位于/var/lib/jenkins,这也是jenkins用户的家目录。 jenkins主程序:/usr/lib/jenkins/jenkins.war jenkins配置文件:/etc/sysconfig/jenkins jenkins启动脚本:/etc/init.d/jenkins jenkins日志目录:/var/log/jenkins 3.启动Jenkins [root@localhost~]#servicejenkinsstart [root@localhost~]#chkconfigjenkinson 4.访问Jenkins,进行初始化设置 http://jenkins_server_ip:8080 二、Jenkins插件安装演示 1.在线安装语言插件 进入Manage Jenkins->Manage Plugins->Available,找到"Locale plugin"并安装,安装过程中会自动处理好插件的依赖。 在线安装插件可能由于网络原因安装失败,记下安装失败的插件和安装顺序,然后使用下面的方法离线安装。 2.离线安装插件 访问网址https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/,搜索上一步安装失败的那些插件,下载下来。 进入Manage Jenkins->Manage Plugins->Advanced,选择插件文件并上传。 注意:要按照依赖顺序一个一个安装插件。 安装好后重启Jenkins(可以通过访问http://jenkins_server_ip:8080/restart重启)。 3.设置语言为中文 进入Manage Jenkins->Configure System,在"Default Language"位置输入zh_CN,保存立即生效。 三、安装SVN插件 1)进入"系统管理"->"插件管理"->"可选插件",找到"Subversion Plug-in"并安装。 2)SVN插件安装好后,我们创建一个新项目。 在项目配置的"源码管理"部分,我们可以对svn仓库进行配置了。 四、安装SSH插件 1)进入"系统管理"->"插件管理"->"可选插件",安装"Publish Over SSH"插件。 2)配置SSH免密码登录到远程服务器 usermod-s/bin/bashjenkins su-jenkins ssh-keygen-trsa 将公钥推送到远程主机: ssh-copy-id-i~/.ssh/id_rsa.pub'-p10022root@192.168.1.211' 3)进入"系统管理"->"系统设置",找到Publish over SSH配置段。 Jenkins SSH Key(插件的全局配置) Passphrase:密码(key的密码,一般我们不会设置) Path to key:key文件(私钥)的路径 Key:将私钥复制到这个框中(与Path to key二选一) Disable exec:禁止远程运行命令 添加SSH远程主机 SSH Server Name:自定义远程主机的名字 Hostname:ssh远程连接的ip地址 Username:用户名 Remote Directory:远程目录 Use password authentication, or use a different key:选中展开就可以配置新的私钥,不使用全局配置。 Port:SSH端口 Timeout (ms):超时时间(毫秒) Disable exec:禁止运行命令(只对当前配置的ssh主机生效) Test Configuration:测试连接,连接成功会返回Success 五、项目配置 1.新建一个项目 新建一个项目,输入项目名称,选择“构建一个自由风格的软件项目”。 2.构建触发器 触发远程构建 远程构建URL: JENKINS_URL/job/JOB_NAME/build?token=TOKEN_NAME 我们可以在URL末尾加上&cause=Cause+Text描述构建原因。 例如: http://192.168.1.203:8080/job/testjob1/build?token=testjob1token 脚本调用方法: curl -v -u 用户名:登录密码 远程构建URL Build after other projects are built 指定的项目完成构建后,才触发此项目的构建。 Build periodically 按指定的时间频率对项目进行构建。 Poll SCM 按指定的时间频率检查源代码仓库的变化,如发现变化则执行构建。 3.构建环境 Send files or execute commands over SSH before the build starts 构建开始前通过ssh给远程主机发送文件或者执行命令。 Send files or execute commands over SSH after the build runs 构建结束后通过ssh给远程主机发送文件或者执行命令。 这两个选项是在安装了“Publish Over SSH”插件后出现的,比较实用。配置介绍: Name:在系统设置里配置的SSH远程主机的名字 Source files:上传的文件或目录(相对于项目工作区的路径,非绝对路径。可以填写多个,默认用逗号分隔) Remove prefix:移除Source files的目录前缀 Remote directory:远程主机的上传目录(如果目录不存在会自动创建) Exec command:要远程执行的命令 Flatten files:只上传文件,不创建目录(除了远程目录) 4.构建步骤 Execute Windows batch command 执行Windows批处理脚本(Jenkins安装在Windows系统中)。 Execute shell 执行Linux shell脚本(Jenkins安装在Linux系统中)。 Invoke Ant JenKins与Ant集成实现项目的自动化构建部署,适用于Ant作为构建工具的项目。 Invoke top-level Maven targets JenKins与Maven集成实现项目的自动化构建部署,适用于Maven作为构建工具的项目。 Send files or execute commands over SSH 通过ssh给远程主机发送文件或者执行命令。“Publish Over SSH”插件的功能。 5.参数化构建 通过参数化构建功能,我们可以为构建程序传递参数,灵活控制构建流程。 在项目配置里点选“参数化构建过程”,然后选择“添加参数”,jenkins提供了多种参数类型: 举个例子,我们添加两个参数: 参数level是Choice类型,可选值1和2,默认值是1。 参数text是String类型,默认值是“hello world”。 参数设置好后,我们可以在构建脚本里引用这些变量。 Windows平台引用方法:%var_name% Linux平台引用方法:${var_name} 配置完整个项目后,我们手动运行构建: 在项目面板的左侧选择“Build with Parameters”,先对参数值进行修改,然后点击“开始构建”。 参数化远程构建URL(通过访问url触发构建): JENKINS_URL/job/JOB_NAME/buildWithParameters?token=TOKEN_NAME&PARM1=VALUE1&PARM2=VALUE2… 例如: http://192.168.1.203:8080/job/testjob1/buildWithParameters?token=testjob1token&level=2&text=hello+world 脚本调用方法: curl -v -u 用户名:登录密码 远程构建URL

优秀的个人博客,低调大师

娱乐城搭建必备服务器SSC、棋牌、网站搭建

很多客户服务器不稳定,卡掉线,被攻击怎么办? 这犯罪团伙还真是能够领会精神,与时俱进,在这么短的时间就学会了互联网+犯罪,以DDOS、CC等网络攻击为要挟实施敲诈勒索。 所谓的DDOS攻击,就是绑架n台僵尸电脑(俗称肉鸡,肉鸡也就是受黑客远程控制的电脑或机器,黑客可以随意操纵它并利用它做任何事情)同时访问网站,从而使网站不能日常运营。这就好像在一条高速公路上,犯罪嫌疑人故意组织大量车辆上高速,使得大量的车辆堵在高速公路上,造成人为阻塞,这样的后果是想上高速的上不来,其破坏的目的也就达到了。 看过这个新闻,让我想起《天下无贼》里的一句台词:二十一世纪什么最贵?人才!可这人才要是用错了地方,岂不成了人渣?做人还是本分点好,出来混早晚要还的,千万别存侥幸心理,不信抬头看,苍天饶过谁?! 专注 网站、棋,牌、菠菜、游戏。聊天室等服务器租用和托管以

优秀的个人博客,低调大师

OSPF基础概述

一、OSPF协议简介 OSPF(Open Shortest Path First,开放最短路径优先)是IETF(Internet Engineering Task Force,互联网工程任务组)组织开发的一个基于链路状态的内部网关协议。工作在IP层,协议号89。 二、RIP的缺陷 1. 最大跳数限制了网络规模(15跳) 2. 以跳数为度量值无法准确判断最优路径 3. 路由更新发送完整路由表消耗网络带宽 4. 收敛速度慢 5. 协议会产生路由自环 三、OSPF初始化流程 1. 建立邻居和邻接关系 1.1 发送hello报文发现和建立邻居关系,组播地址224.0.0.5 说明:必须满足:接口UP;双方接口IP地址在同一网段;双方接口在同一区域等条件。 1.2 选举DR/BDR,建立邻接关系 备注:DR(Designated Router,指定路由器) BDR(Backup Designated Router,备份指定路由器) 选举原因:使路由信息交换更加高速有序 选举范围:每条广播链路(每一个网段)上都需要选举出一个DR和一个BDR 选举规则:(1)优先级数字大的优先,默认优先级都是1;(2)Router-id大的优先 Router-id:定义,Rid,标识路由器的身份;产生方法:(1)手动配置一个IPv4地址格式作为Rid;(2)自动选举,1.在所有环回口中选举IP地址最大的作为Rid;2.在所有物理接口中选举IP地址最大的作为Rid 建议手动配置一个本地环回口的IP地址作为Rid 关系状态:DRother与DR建立邻接关系;DRother与BDR建立邻接关系;DR与BDR建立邻接关系;两个DRother之间保持邻居关系 说明:DRother:既不是DR路由器也不是BDR路由器,剩下的同网段其它路由器 2. 邻接路由器之间交换链路状态信息,实现区域内链路状态数据库同步 备注:邻居关系的路由器不参与链路状态信息交换 相关概念: 链路状态通告:LSA,用来描述路由器的接口、路由条目的相关信息 链路状态数据库:LSDB,存储本地所有LSA 工作流程: (1)向邻接路由器发送DD报文,通告本地LSDB中所有LSA的摘要信息; (2)收到DD后,与本地LSDB对比,向对方发送LSR报文,请求发送本机所需的LSA的完整信息 (3)收到LSR后,把对方所需的LSA的完整信息打包为一条LSU报文,发送至对方 (4)收到LSU后,向对方回复LSAck报文,进行确认 说明:可以看出OSPF路由更新不会发送完整路由表,因此消耗网络带宽较低。 3. 每台路由器根据本机链路状态数据库,计算到达每个目的网段的最优路由,写入路由表 说明:可以看出OSPF路由更新不会发送完整路由表,因此消耗网络带宽较低 四、OSPF报文类型 (1)Hello报文,周期性发送,用来发现和维持OSPF邻居关系;进行DR、BDR的选举 (2)DD报文(Database Description,数据库描述),用于描述本地LSDB中所有LSA的摘要,用于两台路由器进行数据库同步 (3)LSR报文(Link State Request,链路状态请求),向对方请求所需的LSA。两台路由器互相交换DD报文之后,得知对端的路由器有哪些LSA是本地的LSDB所缺少的,这时需要发送LSR报文向对方请求所需的LSA (4)LSU报文(Link State Update,链路状态更新),向对方发送其所需要的LSA (5)LSAck报文(Link State Acknowledgment,链路状态确认),用来对收到的LSA进行确认 五、OSPF分区域管理 1.分区域的原因 加快收敛速度;把网络故障隔离在区域内部 说明:分区后,邻居、邻接关系的建立在各自区域内进行,所以可以加快收敛速度。划分区域后,可以在区域边界路由器上进行路由聚合,以减少通告到其他区域的LSA数量,还可以将网络拓扑变化带来的影响最小化(本区域的OSPF链路故障不会通告到其它区域) 2. 路由器角色 IR(内部路由器),所有接口都处于同一个区域 ABR(区域边界路由器),连接不同区域的路由器 ASBR(自治系统边界路由器),连接外部自治系统的路由器 OSPF分区图示: 3. 区域类型 骨干区域,只能有一个骨干区域,一般是区域0;骨干区域必须是连续的 非骨干区域,非骨干区域必须连接到骨干区域 说明:这样规定的原因主要是为了防环 五、OSPF常用命令 (1)[h3c]ospf 'process id' router-id 'rid' 开启OSPF进程,指定Router-id,进入OSPF协议视图 (2)[h3c-ospf-1]area 'area id' 进入区域视图 (3)[h3c-ospf-1-area 0.0.0.0]network 'ip address' 'wild-mask' 宣告网段 说明:wild-mask(掩码通配符),0对应的部分需要匹配一致,1对应的部分不检查,与子网掩码没有半毛钱关系 (4)[h3c-ospf-1]slient-interface 'interface id' 配置静默接口,OSPF不会在向静默接口上收发协议报文(与RIP静默接口目的一样) (5)[h3c]display ospf peer 查看OSPF邻居关系,FULL:表示邻接关系;2-Way:表示邻居关系 (6)[h3c]display ospf routing 查看OSPF路由 六、OSPF配置实例 1. 配置拓扑 2.配置需求 (1)按照图示配置 IP 地址; (2)按照图示分区域配置 OSPF ,实现全网互通; (3)为了路由结构稳定,要求路由器使用环回口作为 Router-id,ABR 的环回口宣告进骨干区域; 3.配置步骤 (1)按照拓扑所示,配置IP地址 [R1]intLoopBack0 [R1-LoopBack0]ipa1.1.1.132 [R1-LoopBack0]intg0/0 [R1-GigabitEthernet0/0]ipa20.1.1.124 [R1-GigabitEthernet0/0]intg0/2 [R1-GigabitEthernet0/2]ipa10.1.1.224 [R1-GigabitEthernet0/2]quit [R1] [R2]intLoopBack0 [R2-LoopBack0]ipa2.2.2.232 [R2-LoopBack0]intg0/0 [R2-GigabitEthernet0/0]ipa20.1.1.224 [R2-GigabitEthernet0/0]intg0/1 [R2-GigabitEthernet0/1]ipa30.1.1.124 [R2-GigabitEthernet0/1]quit [R2] [R3]interfaceLoopBack0 [R3-LoopBack0]ipa3.3.3.332 [R3-LoopBack0]intg0/1 [R3-GigabitEthernet0/1]ipa30.1.1.224 [R3-GigabitEthernet0/1]intg0/2 [R3-GigabitEthernet0/2]ipa40.1.1.124 [R3-GigabitEthernet0/2]quit [R3] [R4]intLoopBack0 [R4-LoopBack0]ipa4.4.4.432 [R4-LoopBack0]intg0/2 [R4-GigabitEthernet0/2]ipa10.1.1.124 [R4-GigabitEthernet0/2]quit [R4] [R5]intLoopBack0 [R5-LoopBack0]ipa5.5.5.532 [R5-LoopBack0]intg0/2 [R5-GigabitEthernet0/2]ipa40.1.1.224 [R5-GigabitEthernet0/2]quit [R5] (2)按照图示分区域配置 OSPF ,实现全网互通 说明:1. 创建ospf进程可以手动指定router-id,也可以不指定自动生成 2.环回口可以宣告进ospf,也可以不宣告,不影响业务互通;宣告时可以宣告进骨干区域,也可以宣告非骨干区域。有选择困难的建议宣告到骨干区域 [R1]ospf1router-id1.1.1.1 [R1-ospf-1]area0 [R1-ospf-1-area-0.0.0.0]network20.1.1.10.0.0.255 [R1-ospf-1-area-0.0.0.0]network1.1.1.10.0.0.0 [R1-ospf-1-area-0.0.0.0]disthis # area0.0.0.0 network1.1.1.10.0.0.0 network20.1.1.00.0.0.255 # [R1-ospf-1-area-0.0.0.0]quit [R1-ospf-1]area1 [R1-ospf-1-area-0.0.0.1]network10.1.1.20.0.0.255 [R1-ospf-1-area-0.0.0.1]disthis # area0.0.0.1 network10.1.1.00.0.0.255 # [R1-ospf-1-area-0.0.0.1]quit [R1-ospf-1]quit [R1] [R2]ospf1router-id2.2.2.2 [R2-ospf-1-area-0.0.0.0]network2.2.2.20.0.0.0 [R2-ospf-1-area-0.0.0.0]network20.1.1.20.0.0.255 [R2-ospf-1-area-0.0.0.0]network30.1.1.10.0.0.255 [R2-ospf-1-area-0.0.0.0]disthis # area0.0.0.0 network2.2.2.20.0.0.0 network20.1.1.00.0.0.255 network30.1.1.00.0.0.255 # return R2-ospf-1-area-0.0.0.0] quit [R2-ospf-1]quit [R2] [R3]ospf1router-id3.3.3.3 [R3-ospf-1]area0 [R3-ospf-1-area-0.0.0.0]network3.3.3.30.0.0.0 [R3-ospf-1-area-0.0.0.0]network30.1.1.20.0.0.255 [R3-ospf-1-area-0.0.0.0]quit [R3-ospf-1]area2 [R3-ospf-1-area-0.0.0.2]network40.1.1.10.0.0.255 [R3-ospf-1-area-0.0.0.2]quit [R3-ospf-1]quit [R3] [R4]ospf1router-id4.4.4.4 [R4-ospf-1]area1 [R4-ospf-1-area-0.0.0.1]network4.4.4.40.0.0.0 [R4-ospf-1-area-0.0.0.1]network10.1.1.10.0.0.255 [R4-ospf-1-area-0.0.0.1]disthis # area0.0.0.1network4.4.4.40.0.0.0 network10.1.1.00.0.0.255 # return [R4-ospf-1-area-0.0.0.1]quit [R4-ospf-1]quit [R4] [R5]ospf1router-id5.5.5.5 [R5-ospf-1]area2 [R5-ospf-1-area-0.0.0.2]network5.5.5.50.0.0.0 [R5-ospf-1-area-0.0.0.2]network40.1.1.20.0.0.255 [R5-ospf-1]quit [R5] (3)检查是否全网互通 说明:检查 OSPF 是否全网互通,一个是检查邻居关系表,看邻居关系是否正常;另一个是检查路由表,看是否学习到全网路由 这里只展示 R1 的检查结果 检查邻居关系 说明:可以看到,R1 已经学习到了全网所有网段的路由信息,配置完成。 附: 关注我,加微信,获取此次配置工程、更多配置案例,也可以承接远程调试各厂商交换机、路由器、防火墙等私活,欢迎各位老板加微信私聊。

优秀的个人博客,低调大师

Django基础6

1.模板的继承(使用频率较高) 2.模板的导入 3.数据库正向与反向迁移 4.单表查询前期准备 5.django脚本环境 6.单表查询关键字 7.单表查询之神奇的双下线查询 8.多表查询之外键字段的增删改查 1.模板的继承(使用频率较高) 需求: 页面主体不变 部分区域变化 模板继承1.需要现在母版中使用block提前划定区域 {% block 区域名称 %} {% endblock %} 2.在子版中先继承再局部替换 {% extends '母版.html' %} {% block 区域名称 %} {% endblock %} 总结母版一般情况下都应该至少含有三个区域 1.css区域 2.content区域 3.js区域 补充{{ block.super }} 调用母版内容 2.模板的导入(了解) {% include 'myform.html' %} 3.数据库正向与反向迁移 正向迁移(将django中的类导入到MySQL中变成表) python3 manage.py makemigrations python3 manage.py migrate 反向迁移inspectdb 4.单表查询预备知识 from django.db import models # Create your models here. class Books(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) # 日期类型 publish_time = models.DateField(auto_now_add=True) """ auto_now:每次修改数据之后都会自动更新当前时间 auto_now_add:第一次创建数据的时候自动记录时间 """ # publish_time = models.DateTimeField() def __str__(self): return self.title # 只能返回字符串类型的数据 class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # 日期类型 publish_time = models.DateField(auto_now_add=True) publish = models.ForeignKey(to='Publish') authors = models.ManyToManyField(to='Author') def __str__(self): return self.title # 只能返回字符串类型的数据 class Publish(models.Model): title = models.CharField(max_length=32) addr = models.CharField(max_length=64) def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField(to='AuthorDetail') def __str__(self): return self.name class AuthorDetail(models.Model): phone = models.BigIntegerField() addr = models.CharField(max_length=64) def __str__(self): return self.addr 5.django脚本环境 django默认情况下是不允许单独使用某个功能部分(models.py) 解决方法: 1.要么自己新建一个py文件 2.要么使用自带的tests文件 import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day47.settings") import django django.setup() 6.单表查询关键字 # 增 1.create() # models.Books.objects.create(title='三国演义',price=456.23) # models.Books.objects.create(title='水浒传',price=876.45) # models.Books.objects.create(title='聊斋志异',price=123.69) # models.Books.objects.create(title='草堂笔记',price=456.96) # 查 2.all() # res = models.Books.objects.all() # print(res) # QuerySet对象 # print(res.query) # 只要是QuerySet对象就可以点query查看内部SQL语句 # 查 3.filter() # res1 = models.Books.objects.filter() # pk特指当前表的主键字段 # print(res1) # QuerySet对象 # print(res1.query) # print(res1.first()) # 获取列表中第一个数据对象 # print(res1.last()) # 获取列表中最后一个数据对象 # 4.values与5.values_list # res2 = models.Books.objects.values('title','price') # print(res2) # QuerySet对象 可以看成列表套字典 # print(res2.query) # res3 = models.Books.objects.values_list('title','price') # print(res3) # QuerySet对象 可以看成列表套元祖 # print(res3.query) # 6.查 get() 不推荐使用 # res4 = models.Books.objects.get(pk=1) # print(res4) # 数据对象 # res5 = models.Books.objects.get(pk=100) # print(res5) # 数据对象 # res6 = models.Books.objects.filter(pk=100) # print(res6) # 数据对象 # 7.取反 exclude() # res7 = models.Books.objects.exclude(pk=1) # print(res7) # QuerySet # print(res7.query) # 8.排序 order_by() 默认是升序(asc) 字段前面加负号降序(desc) # res8 = models.Books.objects.order_by('price') # res8 = models.Books.objects.order_by('-price') # select * from books order by price desc,price asc; # res8 = models.Books.objects.order_by('-price','price') # print(res8) # QuerySet对象 # print(res8.query) # 9.反转 reverse() 必须先有顺序才可以反转 # res9 = models.Books.objects.all() # res9 = models.Books.objects.order_by('price').reverse() # print(res9) # 10.去重 distinct() # res10 = models.Books.objects.all().distinct() # res10 = models.Books.objects.values('title','price').distinct() # print(res10) # 11.计数 count() # res11 = models.Books.objects.count() # print(res11) # 6 # 12.判断是否有数据 exists() # res12 = models.Books.objects.filter(pk=999).exists() # print(res12) # False # 13.update() # 14.delete() 7.单表查询之神奇的双下划线 # 查询筛选价格的书籍 # res = models.Books.objects.filter(price__gt=200) # print(res) # res1 = models.Books.objects.filter(price__lt=200) # print(res1) # res2 = models.Books.objects.filter(price__gte=456.23) # print(res2) # res3 = models.Books.objects.filter(price__lte=456.23) # print(res3) # 成员运算 # res4 = models.Books.objects.filter(price__in=(456.23,111)) # print(res4) # 范围查询 # res5 = models.Books.objects.filter(price__range=(100,456.23)) # print(res5) # 模糊查询 # 查询书籍名称中含有字母a的书 # res6 = models.Books.objects.filter(title__contains='a') # print(res6) # 区分 # res7 = models.Books.objects.filter(title__icontains='a') # print(res7) # 忽略大小写 # 日期相关 # 查看出版月份是五月的书 # res8 = models.Books.objects.filter(publish_time__month=5) # print(res8) # print(res8.query) # res9 = models.Books.objects.filter(publish_time__year=2021) # print(res9) 8.多表查询之外键字段增删改查 # 增 # models.Book.objects.create(title='三国演义',price=345.43,publish_id=1) # models.Book.objects.create(title='红楼梦',price=678.31,publish_id=2) # publish_obj = models.Publish.objects.filter(pk=2).first() # models.Book.objects.create(title='三国演义',price=345.43,publish=publish_obj) # models.Book.objects.create(title='七龙珠',price=908.43,publish=publish_obj) # 改 # models.Book.objects.filter(pk=2).update(publish_id=1) # models.Book.objects.filter(pk=2).update(publish=publish_obj) # 删 级联更新级联删除 # models.Publish.objects.filter(pk=1).delete() # 多对多 book_obj = models.Book.objects.filter(pk=3).first() # 绑定关系 # book_obj.authors.add(1) # 去书与作者的关系表中绑定关系 # book_obj.authors.add(1,2) # 去书与作者的关系表中绑定关系 # book_obj.authors.add(author_obj1) # book_obj.authors.add(author_obj1,author_obj2) # 修改关系 # book_obj.authors.set([1,]) # book_obj.authors.set([1,2]) # book_obj.authors.set([author_obj,]) # book_obj.authors.set([author_obj1,author_obj2]) # 移除关系 # book_obj.authors.remove(1) # book_obj.authors.remove(1,2) # book_obj.authors.remove(author_obj,) # book_obj.authors.remove(author_obj1,author_obj2) # 清空关系 book_obj.authors.clear()

优秀的个人博客,低调大师

Springboot日志基础

Springboot配置日志配置文件位置 logging.config: classpath:logging/logback.yml Springboot日志分类 log4j: 是apache实现的一个开源日志组件 logback: 同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现 log4j2: 是log4j 1.x和logback的改进版,采用了一些新技术(无锁异步、等等),使得日志的吞吐量、性能比log4j 1.x提高10倍,并解决了一些死锁的bug,而且配置更加简单灵活 slf4j+log4j和直接用log4j的区别 slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback),使用接口的好处是当项目需要更换日志框架的时候,只需要更换jar和配置,不需要更改相关java代码。 import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestSlf4j { //Logger和LoggerFactory导入的是org.slf4j包 private final static Logger logger = LoggerFactory.getLogger(TestSlf4j.class); } log4j、logback、log4j2都是一种日志具体实现框架,所以既可以单独使用也可以结合slf4j一起搭配使用 import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class TestLog4j { // Logger和LogManager导入的是org.apache.logging包 private static final Logger LOG = LogManager.getLogger(TestLog4j.class); } 导入需要使用的jar包(slf4j+log4j2) log4j2 如项目中有导入spring-boot-starter-web依赖包记得去掉spring自带的日志依赖spring-boot-starter-logging <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> springboot项目中需导入log4j2 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> 如果要使用log4j,则把log4j2的坐标替换为下面的这个,依然要排除原有的spring-boot-starter-logging。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> Java引入log4j配置文件 class ImportConfig{ public static void main(String[] args) throws IOException { File file = new File(log4j2); BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); final ConfigurationSource source = new ConfigurationSource(in); Configurator.initialize(null, source); Logger logger = LogManager.getLogger("myLogger"); } } Springboot整合zookeeper log4j2 <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.14</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency>

优秀的个人博客,低调大师

多线程基础

需要了解的概念 并发和并行 并发侧重于任务的交替执行,同一时间只能执行一个任务;而并行是任务的同时执行,统一时间可以有多个任务被执行。 单核CPU与多核CPU下任务表现分别为并发与并行。 临界区 临界区用于表示一种公共资源或是共享数据,可以被多个线程使用,但是同一时间内,只能有一个线程在使用它。一旦临界区资源被占用,其他线程要想使用这个资源,则必须等待。 死锁、饥饿和活锁 死锁 指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 饥饿 饥饿是指线程因为种种原因无法获取所需要的资源,导致一直无法执行。比如它的线程优先级可能太低,而高优先级的线程不断抢占他需要的资源。 活锁 当其他线程要使用临界资源时,如果线程主动放弃资源供其他线程使用,而其它线程也主动放弃来使其他线程使用。这样你让我,我让你,最后无论哪个线程都无法使用资源。 线程的状态 Java 线程有6种状态,其定义在Thread.State中: public class Thread implements Runnable { /** * A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect any operating system thread states. */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or * reenter a synchronized block/method after calling {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } } NEW:初始状态,线程被构建,但是还没有调用start方法; RUNNABLED:运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为RUNNABLED; BLOCKED:阻塞状态,例如进入同步锁中; WAITING:等待状态,例如调用Object.wait、Thread.join(with no timeout)、LockSupport.park等; TIME_WAITING:超时等待状态,例如调用Thread.sleep以及带超时间的Object.wait、Thread.join等,超时以后自动返回; TERMINATED:终止状态,表示当前线程执行完毕。 示意图如下: 线程的基本使用 java中多线程使用方式主要为继承Thread类、实现Runnable接口、实现Callable接口。如果使用线程池,可以使用ExecutorService等。 线程的新建 继承Thread 示例: class ThreadDemo extends Thread { public void run() { System.out.println("run"); } public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); threadDemo.start(); } } Thread类本质上是实现了Runnable接口的一个实例。启动线程的唯一方法就是通过Thread类的 start()实例方法: public class Thread implements Runnable { ... /** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. */ public synchronized void start() { ... try { start0(); started = true; } finally {...} } } start()中最终调用了start0(),而start0()是一个native方法,它会启动一个新线程,并在该线程中执行run方法。 start()执行涉及两个线程:当前线程(threadDemo在该线程中调用自己的start())和新的线程(jvm会在该线程中调用run方法,并使其在在该线程中执行)。 值得注意的是,threadDemo直接调用run()不会开启新的线程,因为没有最终调用start0(),此时仅是在当前线程中普通的方法调用;threadDemo直接调用start()则会开启新线程,并让jvm调用run方法在开启的线程中执行。 另外,多次启动线程是不合法的,尤其是线程执行完成后不能重新启动。 实现Runnable接口 如果自己的类已经继承了其它类,无法直接继承Thread,此时可以实现Runnable接口: class ThreadDemo implements Runnable { public void run() { System.out.println("run"); } public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); new Thread(threadDemo).start(); } } 实现方式虽然变了,但是最终还是要调用start()。 实现Callable接口 当需要新线程提供返回值给主线程时,例如主线程需要依赖该返回值进行后续的处理,此时可以使用Callable方式: class ThreadDemo implements Callable { public String call() { return "run"; } public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask = new FutureTask<>(new ThreadDemo()); new Thread(futureTask).start(); String result = futureTask.get(); System.out.println(result); } } Callable和Runnable Callable方式属于Executor框架的功能类,相对于runable体系: 1. callable在任务结束时可以提供返回值,runnable无法提供; 2. run()不能抛出任何检查型异常,但是,非检查型异常会导致线程终止; 3. 运行callable可以拿到future,future能监视目标线程调用call方法的情况,当调用future的get方法以获取结果时,当前线程就会被阻塞,直到call方法结束返回结果。 对于第二条: 检查型异常(Checked Exception):指编译器要检查这类异常,这类异常的发生通常是难以避免的,编译器强制让开发者去解决掉这类异常(通过throws或try-catch),所以称为检查型异常。如果不处理这类异常则不会通过编译。 例如FileNotFoundException,编译器认为文件找不到是不可避免的,如果不处理这些异常,程序将来肯定会出错,此时编译器会提示捕获并处理这种可能发生的异常,不处理就不能通过编译。 非检查型异常(Unchecked Exception):指编译器不会检查这类异常,编译器认为此类异常不是必须处理的,因此即使不处理这类异常,编译器也不会给出错误提示。 例如数组越界、空指针异常等。 run()不能抛出检查型异常,因为接口定义中就没有throws异常: @FunctionalInterface public interface Runnable { public abstract void run(); } 并且throws是向上级调用者抛出异常,主线程调用start()后,是由jvm去调用run()方法的,因此run()的最终调用者是jvm。 但是线程依然有可能抛出unchecked exception,此类异常抛出时会导致线程终止,但是对于主线程和其他线程则完全感知不到该异常的抛出(当然也无法法catch到该异常),且该异常的抛出对主线程和其他线程完全没有影响。 因此,对于Runnable体系的线程,我们不能捕获在线程中出现的异常,因此无论是checked exception还是unchecked exception,run方法内进行try-catch并处理掉,即: class ThreadDemo0 implements Runnable { public void run() throws Exception { // throws Exception会导致编译不通过 System.out.println(1 / 0); } public static void main(String[] args) { try { ThreadDemo0 threadDemo0 = new ThreadDemo0(); new Thread(threadDemo0).start(); } catch (Exception e) { e.printStackTrace(); // 无法catch到除0异常 } } } java5之后,我们可以通过Executor来解决run()抛出的unchecked exception问题。Thread.UncaughtExceptionHandler是java5的新接口,它允许在每一个Thread对象上添加一个异常处理器: public class Thread implements Runnable { /** * Interface for handlers invoked when a <tt>Thread</tt> abruptly * terminates due to an uncaught exception. * <p>When a thread is about to terminate due to an uncaught exception * the Java Virtual Machine will query the thread for its * <tt>UncaughtExceptionHandler</tt> using * {@link #getUncaughtExceptionHandler} and will invoke the handler's * <tt>uncaughtException</tt> method, passing the thread and the * exception as arguments. * If a thread has not had its <tt>UncaughtExceptionHandler</tt> * explicitly set, then its <tt>ThreadGroup</tt> object acts as its * <tt>UncaughtExceptionHandler</tt>. If the <tt>ThreadGroup</tt> object * has no * special requirements for dealing with the exception, it can forward * the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler * default uncaught exception handler}. * * @see #setDefaultUncaughtExceptionHandler * @see #setUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ @FunctionalInterface public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the * given uncaught exception. * <p>Any exception thrown by this method will be ignored by the * Java Virtual Machine. */ void uncaughtException(Thread t, Throwable e); } } 当线程因未捕获的异常而将要被终止时,则jvm首先查询当前线程是否有UncaughtExceptionHandler处理器,如果有则使用该处理器的uncaughtException()来处理,并将当前线程及其异常作为参数传递过去; 如果没有该处理器,则查看当前线程所在线程组是否设置了UncaughtExceptionHandler,如果已经设置则使用该线程组的UncaughtExceptionHandler来处理; 否则,通过getUncaughtExceptionHandler获取默认处理器: public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } 如果上述处理器都不存在,那么jvm将直接在console中打印Exception的StackTrace信息。 线程的停止 线程停止不能使用Thread.stop(),因为该方法是暴力停止,如果业务执行到一半被stop()终止,则可能会导致数据的不一致。 正确的停止方式应该由实际业务决定,例如: class ThreadDemo0 implements Runnable { private static volatile boolean stop = false; private static volatile int i = 0; public void run() { while(!stop) { System.out.println(i++); } } public static void main(String[] args) { new Thread(new ThreadDemo0()).start(); while(true) { if (i == 5) { stop = true; } } } } 实际上Thread提供了线程中断相关的API: public void Thread.interrupt(); public boolean Thread.isInterrupted(); public static boolean Thread.interrupted(); interrupt()用于通知线程中断,也就是设置中断标志位,中断标志位表示当前线程已经被中断了。isInterrupted()用于判断当前线程是否被中断。静态方法interrupted()也是用于判断当前线程的中断状态,但同时会清除当前线程的中断标志位。 class ThreadDemo0 implements Runnable { private static volatile int i = 0; public void run() { while(!Thread.currentThread().isInterrupted()) { System.out.println(i++); } } public static void main(String[] args) { Thread thread = new Thread(new ThreadDemo0()); thread.start(); while(true) { if (i == 5) { thread.interrupt(); break; } } } } 当发生异常时,interrupt标志位会被复位。 线程等待和通知 wait()和notify()、notifyAll()用于多线程之间的协作,这两个方法都属于Object类,这意味着任何对象都能调用这两个方法。 当在一个对象A调用wait()方法后,当前线程就会在这个对象上等待,直到其他线程调用了对象A的notify()或notifyAll()为止。 如果一个线程调用了object.wait(),那么该线程则进入object对象的等待队列。这个队列中可能会存在多个线程,当object.notify()被调用时,它就会从这个等待队列中随机选择一个线程并将其唤醒。如果执行object.notifyAll()则唤醒队列中所有线程。 需要注意的是,wait()、notify()、notifyAll()必须在synchronized代码块中使用,方法执行前都必须获取目标对象的监视器,当wait()执行时会释放当前的监视器并使得当前线程进入阻塞队列,当notify()被调用时,首先要获取到object的监视器,然后才能去唤醒其他线程。 示例: class ThreadDemo { final static Object OBJECT = new Object(); public static class T1 extends Thread { public void run() { synchronized (OBJECT) { try { System.out.println("t1 start"); OBJECT.wait(); System.out.println("t1 end"); } catch (InterruptedException e) {} } } } public static class T2 extends Thread { public void run() { synchronized (OBJECT) { System.out.println("t2 start"); OBJECT.notify(); System.out.println("t2 end"); try { Thread.sleep(2000); } catch (Exception e) {} } } } public static void main(String[] args) { new T1().start(); new T2().start();; } } 上述代码执行时,t1被唤醒后并不能立即执行,因为t2线程sleep了2秒,在这2s内t2并未释放object的监视器,所以在t2线程sleep了2s后,才会输出t1 end。 这三个方法必须在synchronized代码块中执行,因为这些操作都和监视器相关,wait必须要知道获取谁的监视器,而notify需要知道去唤醒等待在哪里的线程,而synchronized可以提供这个监视器。 监视器(monitor)和锁(lock)的关系: 在jvm中,锁的实现方式就是monitor; entermonitor就是获得某个对象的lock(owner是当前线程); leavemonitor就是释放某个对象的lock。 简单的认为,在object中,monitor就是lock。 线程挂起和继续 线程的挂起和继续执行的方法分别是suspend()和resume(),被挂起的线程,必须要等到resume()操作后才能继续执行。但这两个方法已经被废弃了。 因为suspend()在导致线程暂停的同时,并不会去释放任何锁资源。此时,其他任何线程想要访问被它占用的锁时,都会被阻塞。直到对应的线程上进行了resume()操作,被挂起的线程才能继续。但是,如果resume()操作意外地在suspend()前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且它所占用的锁也不会被释放。 而且,对于被挂起的线程,从它的线程状态上看,居然还是Runnable,这也会严重影响我们对系统当前的判断。 线程join和yield join和yield的部分源码: public class Thread implements Runnable { /** * Waits for this thread to die. */ public final void join() throws InterruptedException { join(0); } /** * Waits at most {@code millis} milliseconds for this thread to * die. A timeout of {@code 0} means to wait forever. */ public final synchronized void join(long millis) throws InterruptedException { ... if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { ... wait(delay); ... } } } /** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. */ public static native void yield(); } join 当前线程调用其他线程的join方法,会阻塞当前线程,直到其他线程执行完毕,才会继续执行。 join()表示无限期的等待,而join(long millis)则指定等待的最大时间,如果超过最大时间则不再等待该线程,继续执行。 从源码中可以发现,join()方法是通过wait()实现的,而join(long millis)则是通过wait(long timeout)实现的。当millis为0 时,会进入while(isAlive())循环,并且只要子线程是活跃的,宿主线程就不停的等待。 join方法会让宿主线程交出CPU执行权,并放弃占有的锁。 yield 调用yield()会让当前线程交出CPU资源,但是交出CPU资源后,该线程仍然会参与争夺下一轮CPU的使用权。但是,yield()不能控制具体的交出CPU的时间。 调用yield()方法并不会让线程进入阻塞状态,也不会释放锁,而是让线程重回就绪状态,它只需要等待重新得到CPU的执行权就又能继续执行了。 参考:java高并发程序设计

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册