首页 文章 精选 留言 我的

精选列表

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

HDFS的基础组成部分及基础操作

HDFS组件结构图解说: 1、如图所示中,NameNode充当master角色,职责包括:管理文档系统的命名空间(namespace);调节客户端访问到需要的文件(存储在DateNode中的文件) 2、DataNodes充当slaves角色,通常情况下,一台机器只部署一个Datenode,用来存储MapReduce程序需要的数据 3、Namenode会定期从DataNodes那里收到Heartbeat和Blockreport反馈 4、Heartbeat反馈用来确保DataNode没有出现功能异常; 5、Blockreport包含DataNode所存储的Block集合 二、HDFS设计原则 1、文件以块(block)方式存储 2、每个块带下远比多数文件系统来的大(预设64M) 3、通过副本机制提高可靠度和读取吞吐量 4、每个区块至少分到三台DataNode上(一般,对namenode进行raid1配置,对datanode进行raid5配置) 5、单一 master (NameNode)来协调存储元数据(metadata) 6、客户端对文件没有缓存机制 (No data caching) 三、NameNode(NN) NameNode主要功能提供名称查询服务,它是一个jetty服务器(一个开源的servlet容器,嵌入式的web服务器) NameNode保存metadate信息包括 文件owership和permissions 文件包含哪些块 Block保存在哪个DataNode(由DataNode启动时上报) 1、NameNode的metadate信息在启动后会加载到内存 2、metadata存储到磁盘文件名为”fsimage” Block的位置信息不会保存到fsimage 四.DataNode(DN) 保存Block 启动DN线程的时候会向NN汇报block信息 hadoop fs -cmd cmd :为具体的操作,基本上于UNIX的命令相同 args:参数 hdfs资源URL格式:scheme://bigdata/path scheme:协议名,file或hdfs bigdata:namenode主机名 path:路径 eg:hdfs://localhost:9000/user/chunk/test.txt 假设已经在core-site.xml设置了fs.default.name=hdfs://localhost:9000, 则仅使用/user/chunk/test.txt即可 hdfs命令示例:存放的数据以文件的形式存储,使用绝对路径来区分每个资源,在创建目录来存储资源时候要加/ #创建目录 hadoop fs -mkidr/myFirstDir #查看创建的目录 hadoop fs -ls /myFirstDir #返回为空,目前还没存放数据 #对当前创建目录存放文件 hadoop fs -put /etc/shadow /myFirstDir #查看目录下的文件 hadoop fs -ls /myFirstDir #复制文件到指定的位置 hadoop fs -get /hadoop目下的文件 /本地文件路径 hadoop fs -get /myFirstDir/shadow /home/#下载shadow到/home目录下 #新建一个空文件 hadoop fs -touchz /myFirstDir/newFile.txt #将hadoop上某个文件重命名 hadoop fs -mv /myFirstDir/newFile.txt /myFirstDir/bigdata.txt #将hadoop指定目录下所有内容保存为一个文件,同时down至本地 hadoop dfs -getmerge /myFirstDir/bigdata.txt /home/a #查看文件里的内容 hadoop fs -cat /myFirstDir/shadow #查看最后1000字节数据 hadoop fs -tail /myFirsDir/shadow #删除文件\目录 hadoop fs -rm -R /myFirstDir/shadow hadoop fs -rm -R /myFirstDir/Secondary #查看HDFS下的文件 hadoop fs -ls / #查看集群数据的信息,登陆master节点查看 http://192.168.1.114:50070 管理与更新 #查看HDFS的基本统计信息 hadoop dfsadmin -report #进出安全模式 hadoop dfsadmin -safemode enter hadoop dfsadmin -safemode leave #节点添加 添加一个新的DataNode节点,先在新加节点上安装好Hadoop, 要和NameNode使用相同的配置(可以直接从NameNode复制),修改$HADOOP_HOME/conf/master文件,加入NameNode主机名。 然后在NameNode节点上修改$HADOOP_HOME/conf/slaves文件,加入新节点名,再建立新加节点无密码的SSH连接,运行启动命令为: /bin/start-all.sh #负载均衡 HDFS的数据在各个DataNode中的分布可能很不均匀,尤其是在DataNode节点出现故障或新增DataNode节点时。 新增数据块时NameNode对DataNode节点的选择策略也有可能导致数据块分布不均匀。 用户可以使用命令重新平衡DataNode上的数据块的分布: start-balancer.sh 本文转自 DBAspace 51CTO博客,原文链接:http://blog.51cto.com/dbaspace/1874685

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

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高并发程序设计

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

linux基础-1

1、常见Linux的发行版有哪些?并描述不同发行版之间的联系与区别SUSEUbuntumintRHELCentOS中标麒麟FedoraArchLinuxGentooAndroid所有版本都有相同的linux内核由不同的组织发行,因为组织不同所以系统的特性也不同2、打印显示当前时间,格式是:20181209211008 date +%G%m%d%N3、实现晚上20:30自动关机,并提示用户shutdown -s 20:304、总结历史命令管理以及用法history命令,可以显示曾经使用过的命令,系统会把历史命令写到用户的家目录下的一个文件里,用户登录后,系统会读取这个文件里的所有历史命令到内存。而history这个命令可以吧历史命令显示出来,显示出来的历史命令前有序号,可以通过!n 的方式执行历史命令5、总结Linux帮助用法linux上有帮助相关的命令有:whatiscommand --helpman and info/usr/share/doc/Red Hat documentation其他途径:网上搜索,与求助

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

kubernetes基础使用

官方文档:https://v1-13.docs.kubernetes.io/zh/docs/tutorials/kubernetes-basics/pods生命周期:https://v1-13.docs.kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/配置: https://v1-13.docs.kubernetes.io/docs/concepts/configuration/labels: https://v1-13.docs.kubernetes.io/docs/concepts/overview/working-with-objects/labels/Deployments: https://v1-13.docs.kubernetes.io/docs/concepts/workloads/controllers/deployment/Services: https://v1-13.docs.kubernetes.io/docs/concepts/services-networking/service/Scaling: https://v1-13.docs.kubernetes.io/docs/tutorials/kubernetes-basics/scale/scale-intro/autoscale: https://v1-13.docs.kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ #查看k8s版本 kubectl version #查看所有的节点 kubectl get nodes #获取namespace kubectl get namespace #查看namespace为online的pod kubectl get pods --namespace=online #部署应用 kubectl run kubernetes-bootcamp --image=jocatalin/kubernetes-bootcamp:v1 --port=8080 #查看部署应用列表 kubectl get deployments --namespace= #查看所有pod kubectl get pods --namespace= #获取执行中的pod并设置成环境变量POD_NAME export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}') echo Name of the Pod: $POD_NAME #访问pod curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/ kubectl 故障排查 kubectl get pods --namespace= #查看resources列表 kubectl describe pods --namespace= #查看具体的resources情况 kubectl logs $POD_NAME --namespace= #查看pod里的容器日志 kubectl exec --namespace= #在pod里的容器里执行命令 kubectl exec $POD_NAME env --namespace= #查看pod环境变量 kubectl exec -ti $POD_NAME bash --namespace= #进入pod终端 Service & labelsService type 类型 说明 ClusterIP 默认选项,只能集群内部访问 NodePort 使用NAT暴露每个节点的相同端口并使用<NodeIP>:<NodePort> 方式让集群外部访问ClusterIP的超集 LoadBalancer 创建负载均衡器分配一个固定的IPNodePort的超集 ExternalName 使用域名解析的CNAME来指定名字或者手动自定名字不使用proxy 需要v1.7及以上版本的kube-dns 更多的内容参考:https://v1-13.docs.kubernetes.io/docs/tutorials/services/source-ip/ https://v1-13.docs.kubernetes.io/docs/concepts/services-networking/connect-applications-service/ labelsservice通过labels把一组pods管理起来。比如通过labels指定开发、测试、生产环境比如打版本tag使用标签分类 #获取service kubectl get services --namespace= #指定service type kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080 #查看指定services信息 kubectl describe services/kubernetes-bootcamp --namespace= #设置NODE_PORT端口为环境变量 export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}') echo NODE_PORT=$NODE_PORT #查看部署情况 kubectl describe deployment --namespace= #-l根据label查询pods kubectl get pods -l run=kubernetes-bootcamp --namespace= #-l根据label查询services kubectl get services -l run=kubernetes-bootcamp --namespace= export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}') echo Name of the Pod: $POD_NAME #设置label,label是key/value对 kubectl label pod $POD_NAME app=v1 #根据label查询pods kubectl get pods -l app=v1 --namespace= #删除service kubectl delete service -l run=kubernetes-bootcamp --namespace= #pod里执行命令 kubectl exec -ti $POD_NAME curl localhost:8080 --namespace= 扩缩容 kubectl get deployments #扩容成4个 kubectl scale deployments/kubernetes-bootcamp --replicas=4 #查看pod是否变成4个 kubectl get pods -o wide #查看deployments详情 kubectl describe deployments/kubernetes-bootcamp #设置环境变量 export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}') echo NODE_PORT=$NODE_PORT #缩容 kubectl scale deployments/kubernetes-bootcamp --replicas=2 滚动升级 #查看deployments kubectl get deployments kubectl get pods kubectl describe pods #滚动升级 kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2 #查看升级是否成功 kubectl describe services/kubernetes-bootcamp #查看升级状态 kubectl rollout status deployments/kubernetes-bootcamp #升级一个不存在的版本,让升级失败。 kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10 #查看pods是否失败 kubectl get pods #查看失败日志 kubectl logs $POD_NAME #回退版本,然后用上面的命令查看是否回退成功 kubectl rollout undo deployments/kubernetes-bootcamp 根据上面的内容部署官网的例子:https://kubernetes.io/docs/tutorials/stateless-application/guestbook/image 从docker hub 上找,先pull下来 #在有问题的node上执行以下命令 查看问题 journalctl -f -u kubelet

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

Javascript基础之-this

this应该算是前期比较容易混淆的一个关键字了,在这里,我就打算按照我的理解来说一下 首先呢,this的值是跟运行时被调用的位置相关的,而不是词法作用域。 也就是说,他的绑定的值很可能是动态的,不同的调用位置,很可能值就不一样,比如说: function foo() { console.log(this.a); } var a = 2; var obj = { a: 3, foo, } foo(); //2 obj.foo(); //3 var c = obj.foo; c(); //2 然后你会惊讶的发现,foo()通过不同的形式调用,他最终的值是不一样的,咱们这一节主要就来讲一讲他们里面的这些绑定的规则 默认绑定 独立的函数调用,它的作用域是作用在全局的,这种绑定规则最常见并且是优先级最低的一种绑定规则。来一个例子 function foo() { console.log(this.a); //2 console.log(this === window); //true } var a = 2; foo(); 在浏览器中执行,会发现,如果foo()作为一个函数单独调用,那么this指向的就是全局对象windows。 但是呢,也有一种例外,如果函数使用的严格模式的话,那么全局对象讲无法使用默认绑定,this将会绑定到undefined,还是刚才的例子: function foo() { "use strict" console.log(this.a); console.log(this === window); } var a = 2; foo(); //Uncaught TypeError: Cannot read property 'a' of undefined 可以看到,foo直接抛出一个error,说this是undefined,取不到a,如果用到严格模式的时候,可以稍微注意下这个区别 咱们刚刚的例子,说的是函数内用严格模式的话,讲无法使用默认绑定,那么如果是在函数调用位置用严格模式,会受影响吗 function foo() { console.log(this.a); //2 console.log(this === window); //true } var a = 2; (function () { "use strict" foo(); })(); 很明显,可以看出,是不受影响的,还有一个问题,如果把var定义变成let会怎么样呢 function foo() { console.log(this.a); //undefined console.log(this === window); //true } let a = 2; (function () { "use strict" foo(); })(); 也就是说,let定义并不会加到全局变量中,这个和var的区别需要稍微留意一下 隐式绑定 这一条规则考虑的是是否有上下文对象,举个例子 function foo() { console.log(this.a); //2 console.log(this === obj); // true } var obj = { a: 2, foo, } obj.foo(); 在这种情况下,foo会使用obj的上下文对象来引用函数,此时的this指向的就是obj。 那么如果是嵌套对象这种的,会怎么处理呢? function foo() { console.log(this.a); // 1 console.log(this === obj) // true console.log(this === obj1) // fale } var obj = { a: 1, foo, }; var obj1 = { a: 2, obj, } obj1.obj.foo(); 可以看到,实际上此时foo中的this指向了离他最近的对象obj,也就是说,对象属性引用链的最后一层会影响它的调用位置 还有一种情况。 function foo() { console.log(this.a); } var obj = { a: 3, foo, } var a = 2; var f = obj.foo; f(); // 2 这种情况下,虽然f是obj.foo的引用,但是最后依然用的是默认绑定,而不是隐式绑定,所以说呢,咱们可以这么理解,虽然他是对象中函数的引用,但是在执行的时候,调用的方式确是函数调用的方式,不是对象.属性()的方式,这块在使用的时候需要注意一下 显式绑定 很简单,显式绑定就是手动直接改变其this指向的一种方式,这个主要是用几种api来实现的 function foo() { console.log(this.a); } var obj = { a: 2, }; foo.call(obj); foo.apply(obj); var f = foo.bind(obj); f(); 这个就是显式绑定的常见的三种形式 这三种形式的话,call和apply差不多,就不展开了,详情看文档就行,咱们仔细看一下bind函数,来在看一个例子 function foo(num1, num2) { console.log(num1 + num2); //5 console.log(this === obj); // true } var obj = { a: 1, }; var f = foo.bind(obj, 2); f(3); 这个可以看出,bind的用法和call以及apply还是有一些差别的。 注意,如果显式绑定传入的是null或者是undefined的话,将会使用默认绑定规则,举个例子 function foo() { console.log( this.a ); } var a = 2; foo.call( null ); // 2 new绑定 这个主要是new 函数()的绑定形式的,这个的this指向的是函数构造出的那个对象,new的时候,它的执行过程可以大概执行以下几步 创建一个全新的对象 这个新对象会被执行prototype连接 新对象绑定到函数调用的this 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象 注意最后一句话,如果函数没有返回对象,那么就会返回函数调用的新对象,如果返回了一个对象呢,那么结果就是直接返回这个对象 var abc = { a: 10 } function foo() { return abc; } foo.prototype = { b: 20 }; var a = new foo(); console.log(a === abc); // true console.log(a.b); // undefined function bar() { this.a = 10; } bar.prototype = { b: 20 }; var b = new bar(); console.log(b.a); // 10 console.log(b.b); // 20 以上四种常见的this绑定方式已经介绍的差不多了,那么问题来了,谁的优先级更高呢 很明显,默认绑定的优先级最低,就不展开说了 显式绑定与隐式绑定哪个优先级高呢,举个例子 function foo() { console.log( this. a ); } var obj1 = { a: 2, foo: foo }; var obj2 = { a: 3, foo: foo }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call( obj2 ); // 3 obj2.foo.call( obj1 ); // 2 很明显,显式绑定优先级要高于隐式绑定 那么new绑定和显式绑定谁更高呢,来再看个例子 function foo(something) { this.a = something; } var obj1 = {}; var bar = foo.bind( obj1 ); bar( 2 ); console.log( obj1.a ); // 2 var baz = new bar(3); console.log( obj1.a ); // 2 console.log( baz.a ); // 3 很明显,new的优先级会更高 所以,这样的话,优先级排序就出来了,分别为 new绑定 > 显式绑定 > 隐式绑定 > 默认绑定 以上就是大家常用到的几种,不过es6出现了一种箭头函数,箭头函数理论上不能应用上面四种的任意一种,它的this是根据代码的词法作用域来走的,比如说 function foo() { return (a) => { console.log( this.a ); }; } var obj1 = { a: 2 }; var obj2 = { a: 3 }; var bar = foo.call( obj1 ); bar.call( obj2 ); // 2 结果已经很明显了, 好了,以上内容就讲完了,通过我的一些讲解,是不是有一点理解了呢,如果有任何的不同意见,欢迎讨论哦 参考书籍《你不知道的Javascript》上卷 转载自:http://www.lht.ren/article/4/

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

Kotlin 基础语法

Kotlin 文件以 .kt 为后缀。 包声明 包的声明应处于源文件顶部: package my.demo import java.util.* // …… kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。如果没有指定包,默认为default包。 默认导入 有多个包会默认导入到每个 Kotlin 文件中: kotlin.* kotlin.annotation.* kotlin.collections.* kotlin.comparisons.* kotlin.io.* kotlin.ranges.* kotlin.sequences.* kotlin.text.* 函数定义 函数定义使用关键字 fun,参数格式为: 参数 : 类型,下面给出几个例子 //带有两个 Int 参数、返回 Int 的函数 fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int return a + b } 表达式作为函数体,返回类型自动推断: fun sum(a: Int, b: Int) = a + b public fun sum(a: Int, b: Int): Int = a + b // public 方法则必须明确写出返回类型 无返回值的函数(类似Java中的void): fun printSum(a: Int, b: Int): Unit { println("sum of $a and $b is ${a + b}") } // 如果是返回 Unit类型,则可以省略(对于public方法也是这样): public fun printSum(a: Int, b: Int) { print(a + b) } 可变长参数函数 函数的变长参数可以用vararg关键字进行标识: fun vars(vararg v:Int){ for(vt in v){ print(vt) } } // 测试 fun main(args: Array<String>) { vars(1,2,3,4,5) // 输出12345 } lambda(匿名函数) lambda表达式使用实例: // 测试 fun main(args: Array<String>) { val sumLambda: (Int, Int) -> Int = {x,y -> x+y} println(sumLambda(1,2)) // 输出 3 } 定义常量与变量 不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量) val <标识符> : <类型> = <初始化值> 可变变量定义:var 关键字 var <标识符> : <类型> = <初始化值> 常量与变量都可以没有初始化值,但是在引用前必须初始化 编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。 val a: Int = 1 // 立即赋值 val b = 1 // 系统自动推断变量类型为Int val c: Int // 如果不在声明时初始化,则必须提供变量类型 c = 1 // 明确赋值 var x = 5 // 系统自动推断变量类型为Int x += 1 // 变量可修改 注释 Kotlin 支持单行和多行注释,实例如下: // 这是一个单行注释 /* 这是一个多行的 块注释。 */ 与 Java 不同, Kotlin 中的块注释允许嵌套。 字符串模板 $ 表示一个变量名或者变量值 $varName 表示变量值 ${varName.fun()} 表示变量的方法返回值: var a = 1 // 模板中的简单名称: val s1 = "a is $a" a = 2 // 模板中的任意表达式: val s2 = "${s1.replace("is", "was")}, but now is $a" NULL检查机制 Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理 //类型后面加?表示可为空 var age: String? = "23" //抛出空指针异常 val ages = age!!.toInt() //不做处理返回 null val ages1 = age?.toInt() //age为空返回-1 val ages2 = age?.toInt() ?: -1 当一个引用可能为 null 值时, 必须在声明处的类型后添加?来标识该引用可为空 当 str 中的字符串内容不是一个整数时, 返回 null: fun parseInt(str: String): Int? { // ... } 以下实例演示如何使用一个返回值可为 null 的函数: fun printProduct(arg1: String, arg2: String) { val x = parseInt(arg1) val y = parseInt(arg2) // 直接使用 `x * y` 会导致错误, 因为它们可能为 null. if (x != null && y != null) { // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量 print(x * y) } } 或者 // …… if (x == null) { println("Wrong number format in arg1: '$arg1'") return } if (y == null) { println("Wrong number format in arg2: '$arg2'") return } ​ // 在空检测后,x 与 y 会自动转换为非空值 println(x * y) 类型检测及自动类型转换 我们可以使用 is 运算符检测一个表达式是否是某类型的一个实例(类似于Java中的instanceof关键字)。如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换: fun getStringLength(obj: Any): Int? { if (obj is String) { // 做过类型判断以后,obj会被系统自动转换为String类型 return obj.length } // 在离开类型检测分支后,`obj` 仍然是 `Any` 类型 return null } 在这里还有一种方法,与Java中instanceof不同,使用!is fun getStringLength(obj: Any): Int? { fun getStringLength(obj: Any): Int? { if (obj !is String) return null // `obj` 在这一分支自动转换为 `String` return obj.length } 甚至还可以 fun getStringLength(obj: Any): Int? { // 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String` if (obj is String && obj.length > 0) return obj.length return null } 区间 区间表达式由具有操作符形式..的 rangeTo 函数辅以 in 和 !in 形成。 区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例: for (i in 1..4) print(i) // 输出“1234” for (i in 4..1) print(i) // 什么都不输出 if (i in 1..10) { // 等同于 1 <= i && i <= 10 println(i) } // 使用 step 指定步长 for (i in 1..4 step 2) print(i) // 输出“13” for (i in 4 downTo 1 step 2) print(i) // 输出“42” // 使用 until 函数排除结束元素 for (i in 1 until 10) { // i in [1, 10) 排除了 10 println(i) } 实例测试 fun main(args: Array<String>) { print("循环输出:") for (i in 1..4) print(i) // 输出“1234” println("\n----------------") print("设置步长:") for (i in 1..4 step 2) print(i) // 输出“13” println("\n----------------") print("使用 downTo:") for (i in 4 downTo 1 step 2) print(i) // 输出“42” println("\n----------------") print("使用 until:") // 使用 until 函数排除结束元素 for (i in 1 until 4) { // i in [1, 4) 排除了 4 print(i) } println("\n----------------") } 输出结果: 循环输出:1234 ---------------- 设置步长:13 ---------------- 使用 downTo:42 ---------------- 使用 until:123 ---------------- 参考: http://www.runoob.com/kotlin/kotlin-basic-syntax.html https://www.kotlincn.net/docs/reference/basic-syntax.html

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

Java基础1.1

虚拟机:vm,java虚拟机jvm。 C语言:写源码;用文本文件写的(写出来就是一个文本文件)。 源码————编译————可执行程序。 运行平台(系统+硬件)。 C;优点:与系统接触近,可以操作系统和系统API 缺点:程序不可跨平台,内存管理十分的麻烦。 内存(运行内存)又称闪存 ———速度十分的快——(一旦掉电数据全部消失)服务器(数据服务中心)服务器运行时间很长。内存运行完要回收 Java优化:如何跨平台(在所有操作系统上运行),如何管理内存(让所有的程序员不在头疼如何管理内存) 怎样跨平台; 三大系统的内核不一样:如何让程序可以跨平台运行那【源码】————【字节码】—————【翻译官】(虚拟机),字节码交给翻译官 怎样优化内存:C语言————编译——-exe————运行 虚拟机就像一个鱼缸: 你要操作内存的话交个容器,虚拟机就像是一个管家;你不用管内存,交给管家就行了,你想要内存问这个管家要就行了。 为什么要形成字节码: 把源码转换为字节码,能让虚拟机很快的认识,运行速度就快。(加快运行速度在运行的时候不用再次解析代码了)优化速度。

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

Java基础1.2

java 编译环节 源码 public .... /// 代码 文件名字后缀用java 全小写 文件1字母大写 (可以用中文命名)比较麻烦 网页下载的只是将虚拟机文件解压出去,不是java环境安装 Java编译环节虚拟机中找到一个程序。叫(javac.exe)程序形成字节码 生成一个字节码文件 如何搭建Java开发环境 :j a v a开发运行流程 java编译环境;写入代码 1名字 文件名字后缀用j a v a 全小写(规定)文件 1字母大写(可以用中文)不太好弄。 运行(java exe)程序并传递你写的文件的源码路径路径,告诉源码路径(打开cmd)磁盘管理系统 编译时时要敲空格 空格表示参数的分割 不空的话表示参数和路径是1(起).的。(不然就会报错)前面是要运行的程序 给程序传递的数据(告诉java路径要他进行编译) 如何让J a v a虚拟机运行我们的字节码 文件名,字节码文件要存在。

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

Java基础1.3

什么是批处理:批处理就是多个dos命令组成的,双击可执行里面的命令。(微软系统) 批处理:桌面文件以双击就能打开,而java一双击是打不开的因为java是一个class文件他需要虚拟机得运行才能打开。 Java不擅长做桌面程序你要是非要做你就得写一个简单的批处理操作。 run.bat:在批处理文件中运行你要运行的文件(java,javaw)加文件名。 bat(批处理的后缀)批处理就是dos的命令。javaw窗口文件运行,使用java不能关闭dos的窗口就是关闭程序的运行,这里是指窗口件。start(开启命令)犹如在dos中运行程序, 要想将你写的程序传播出去,就需要将源码(文件名.java)字节码(文件名.class)批处理(文件名.bat) 还要带上jdk 并且在批处理 bat文件中配置临时环境变量 , JAVA_HOME配置的是子目录前的路径 在配置path的时候不要在配置目录了只需要引入JAVA_HOME即可 set path=./jdk-10.0.2./bin()当前目录显得bin目录 .(代表当前目录下执行) set JAVA_HOME =./jdk-10.0.2(当前路径下的jdk) .(代表当前目录先执行) set CLASSPATH= . ;(当前目录) 这里有语法规定当前目录是(.;) 批处理就是字一个记事本中写执行代码让系统顺着去执行。 Java程序永远离不开jdk 就算你生成了exe也需要jdk才能运行。 如果想直接运行也可以就需要通过一个工具(exej4)这个工具可以直接将java打包成exe可执行程序。(这个你看到的只是假象 他只是做了一个批处理) 所以还不如我们自己写一个批处理

资源下载

更多资源
Mario

Mario

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

Nacos

Nacos

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册