<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Are you kidding me</title>
    <description>我是一个java码农，2013年开始工作至今，主要从事业务系统开发，涉及的技术：Openfire，Activiti5，Spring，Redis，RabbitMQ等。最近从事微服务相关开发。</description>
    <link>https://wengwh.github.io/</link>
    <atom:link href="https://wengwh.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 21 Dec 2020 08:29:30 +0000</pubDate>
    <lastBuildDate>Mon, 21 Dec 2020 08:29:30 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>apache-druid生产使用问题盘点</title>
        <description>&lt;ul&gt;
  &lt;li&gt;问题：查询时间范围大，段数据过多，导致集群资源飙升，影响其他查询&lt;/li&gt;
  &lt;li&gt;解决：在查询前，根据时间范围查询涉及的段大小，对于过大查询做屏蔽过滤&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：前端查询大开销查询已经超时，druid还是会继续执行，无法释放资源&lt;/li&gt;
  &lt;li&gt;解决：使用druid提供的queryId，查询的时候传入，超时调用delete取消查询&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：前端使用分级多次查询，全部查询，对于多维度产生几万个以上的子查询，线程池占用，影响其他用户查询&lt;/li&gt;
  &lt;li&gt;解决：使用权重线程池，同时超时之后释放全部未执行的子查询资源，开发加载控制类，对于满足条数的结果直接返回，不再做后续查询&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：两大业务需要使用druid，经常一方的大查询影响了另一方&lt;/li&gt;
  &lt;li&gt;解决：使用router和broker的路由规则，对集群做划分：druid.router.tierToBrokerMap&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：仓库大部分查询都在最近1年时间，但是保存的时间范围是3年以上，查询效率不高&lt;/li&gt;
  &lt;li&gt;解决：对仓库设置策略，做冷热分割，对1年数据放入热节点，热节点部署多机器&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：druid节点jvm配置，设置过大堆内，堆外内存，导致系统空闲内存少mmap无法快速响应&lt;/li&gt;
  &lt;li&gt;解决：调整jvm配置，堆不超过24G，给予较大的空闲内存&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：JVM疯狂Full GC，释放不了空间，导致全部查询卡顿&lt;/li&gt;
  &lt;li&gt;解决：配置了监控，但是influxdb数据库挂了，导致监控数据长存在内存中，关闭监控，或者维护好influxdb&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：middleManager由于实时任务需要较大的JVM内存，导致全部任务配置都提高，浪费资源&lt;/li&gt;
  &lt;li&gt;解决：使用workerCategorySpec划分任务类型归属的机器，对不同类型设置不同open的jvm&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：middleManager的任务个数上限分配过大，导致清洗过多任务时，对机器内存超出，historical节点oom挂掉&lt;/li&gt;
  &lt;li&gt;解决：根据机器资源，设置druid.worker.capacity，做到不会触发机器瓶颈&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：实时任务和离线任务并行，新版本出现org.apache.druid.java.util.common.ISE: Could not allocate segment for row with timestamp&lt;/li&gt;
  &lt;li&gt;解决：因为分区类型不同，实时任务无法锁定段，在离线任务跑完之后，执行一次compact任务&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;ul&gt;
  &lt;li&gt;问题：仓库存在过多冗余维度，每个仓库都存在玩家相关维度，使用lookup数据太大无法存放，影响仓库清洗和存储&lt;/li&gt;
  &lt;li&gt;解决：建议使用join解决&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
</description>
        <pubDate>Tue, 23 Jun 2020 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/apache-druid/2020/06/23/apache-druid%E7%94%9F%E4%BA%A7%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E7%9B%98%E7%82%B9.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/apache-druid/2020/06/23/apache-druid%E7%94%9F%E4%BA%A7%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E7%9B%98%E7%82%B9.html</guid>
        
        <category>apache-druid</category>
        
        
        <category>apache-druid</category>
        
      </item>
    
      <item>
        <title>2019年终感悟</title>
        <description>&lt;p&gt;2019年结束，到新公司的第二年，整体还行，待遇福利等都有提高，不过在工作的难度上面可能并没有太大的挑战性，在私人生活也有一个大的进展。&lt;/p&gt;

&lt;p&gt;2月，过完年回来，整备开始开发去年年底就安排的一个比较主要的需求，翻译plywood，在使用druid中间件上面，前端应用使用了imply的组件，不过由于解析语法使用的js编写的plywood，对于公司目前来说，会出现高并发nodejs堵死等问题，加上公司熟悉js的人较少，所以决定要java实现一套。一开始可能想自己重写一套，不过看了整个plywood的代码后发现逻辑不简单，涉及的公式等都很多，所以自己采用了一种最粗的办法，直接js转java写一遍，同时理解里面的逻辑。整个2月基本都在做翻译工作。&lt;/p&gt;

&lt;p&gt;3-4月，一直开发2月的plywood翻译工作，基本都翻译完成，也运行成功，从当前版本来看，其实对于plywood的优化不大，但是唯一的作用是java版本实现之后，后续的新需求优化会比较好进行。在翻译的过程，看了外国人写的源码，就算是js写的项目，也做到了比较好的代码规划和分类，确实是很多可以借鉴的地方。&lt;/p&gt;

&lt;p&gt;5月，主要工作需求把去年的广告各渠道的采集多接入新的指标，对于每个渠道的新增指标，由于各个渠道商的接口不一样，很多指标不一定存在，不过总体来说难度不大，就是多看api文档。同时这个月由于有一个紧急需求，需要做活动推荐，不过总体来说只是礼包推荐的翻版，开发起来也是比较顺利。这个月还有一件人生大事就是结婚，在5月底的时候完成了人生大事，生活步入了一个新的轨道。相信办过结婚的人都知道，婚前的事情繁忙，因为考虑到想后面再去度蜜月，所以就请假了3天，在婚前一段时间准备结婚的事情。这段时间确实也比较忙碌。&lt;/p&gt;

&lt;p&gt;6月，继续广告采集的需求，新增一个采集渠道，居然是俄罗斯的mytarget，同时对于以前的推荐系统多接入了新的游戏项目。整体来说这个月工作比较简单，强度也不大。不过公司的工作强度确实比网络上面的996什么的来说轻松不少，这一点来说公司确实很好。&lt;/p&gt;

&lt;p&gt;7月，在年初翻译的plywood版本上线之后，还是会出现查询卡顿的问题，通过排查，发现很多都是冗余查询，加上加大了并发，druid撑不住。由于机器就那么多，所以第一步优化就是加入缓存，通过缓存减少查询druid的次数。同时开始优化以前的需求，比如导出全量查询结果，因为默认情况下面我们查询是自带limit，防止一些大维度撑爆内存，在开发过程中，思考如何实现，由于druid不支持limit 10,20，只支持从头开始的limit,所以考虑到了假分页，通过过滤条件和排序来做，不过这样需要自己来优化查询的表达式。总的来说开发还算顺利&lt;/p&gt;

&lt;p&gt;8月，这个月还是继续之前开发的项目做需要优化等工作，还是在广告采集，推荐系统和plywood三个模块上面做一些优化和开发，整体没什么特别。&lt;/p&gt;

&lt;p&gt;9月，由于老婆公司说半年内要用完婚假，所以这个月我们开始探讨度蜜月去哪里，一开始考虑了斐济岛和欧洲，看完别人旅游攻略，发现支出实在太高，加上签证可能不好通过，就放弃了。最后选择了日本，然后就是签证，选择旅游地方。后面在9月中旬去了日本11天，大版进东京出，还在中间去了伊豆半岛。旅游还是比较轻松的，因为是自助游，我们基本是走走停停，靠蹩脚的英语与人交流，不过日本大城市也基本支持中文。对于忙碌了这么久出来旅游一趟，确实可以让心情放松不少。&lt;/p&gt;

&lt;p&gt;10-11月，开发问卷系统，负责接口对接部分，基本工作是对接公司宁外部门的接口，这里吐槽一下，因为使用的语言不同，接口使用http，肯定对接没什么问题，不过不知道为什么它们的接口，没有按照http的编码方式进行传输，对接起来也是很麻烦。然后就是继续优化plwood的需求，支持新的公式，让仓库组可以更好使用druid。同时开始做一些druid上面的优化，第一点就是lookup支持hbase的数据加载，第二点就是接入快手优化的精确去重。druid也是java开发的组件，使用的公司也很多，都是拿来做分析使用，对于优化有很多地方可以借鉴。&lt;/p&gt;

&lt;p&gt;12月，快年底了，基本的工作就是一些日常的需求，优化开发。然后思考明年的工作计划，因为公司是对内系统，在后面没有什么新的系统需求的情况下，明年的工作计划需要我们自己思考，可能明年的重点还是优化druid。&lt;/p&gt;

&lt;p&gt;在公司的第二年，工作更加从容，今年主要是接触了druid这个组件，时间也过很快，年初还在翻译plywood，明年的计划居然还是和它相关。&lt;/p&gt;

&lt;p&gt;今年博客依然没有更新，在京东买了好几本书，目前再看effective Java，感觉自己对代码的编写还是比较有要求。不过自己在大数据这块的学习还是不够，目前也只是停留在一些常用的使用上面，感觉对以后的提升不大，希望2020年能有大的提高。&lt;/p&gt;

&lt;p&gt;再说说日常生活，结婚之后，就开始了买房的打算，从云看房到实地考察，逐渐的了解了房地产的情况，明白了户型的差别，社区的差别，新房的认购流程，销售的套路等等，不过今年依然没有买下房，2020年争取买到自己的房子。&lt;/p&gt;

&lt;p&gt;在今年看到自媒体很火，自己也刷了B站很久，尝试拍了一些视频，不过播放量什么都是低的可怕，后来就没坚持下去了，下半年由于要买房，了解了很多房产的相关知识，同时在我的城市看了一些房子。然后在头条发了一些文章，发现还有不少人评论关注，后续看看能不能坚持下去。&lt;/p&gt;

&lt;p&gt;对明年的期望，工作上在大数据这块能有提高，能坚持写自己的头条文章，粉丝争取破5000，生活上，希望可以买到符合自己心意的房子。希望自己家人都身体健康。&lt;/p&gt;

</description>
        <pubDate>Wed, 12 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2020/02/12/2019%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2020/02/12/2019%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</guid>
        
        <category>工作</category>
        
        
        <category>工作</category>
        
      </item>
    
      <item>
        <title>大型网站技术架构读书笔记</title>
        <description>&lt;h2 id=&quot;一大型网站架构演化&quot;&gt;一、大型网站架构演化&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;应用服务器和数据服务器分离
    &lt;ul&gt;
      &lt;li&gt;当业务量增大后，开始对服务器做分离，应用服务器处理大量业务，更好的cpu，数据库服务器要数据检索和缓存，需要更快的硬盘和更大的内存，文件服务器需要存储大量用户文件，更大的硬盘。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;二八定律：80%业务集中访问在20%的数据上
    &lt;ul&gt;
      &lt;li&gt;引入缓存机制，减少数据库的访问压力&lt;/li&gt;
      &lt;li&gt;缓存分2种：缓存在应用服务器上的本地缓存和缓存在专门的分布式缓存服务器上的远程缓存&lt;/li&gt;
      &lt;li&gt;本地缓存访问速度更快，但会和应用程序争用内存。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;集群改善并发处理能力
    &lt;ul&gt;
      &lt;li&gt;通过负载均衡调度服务器，将访问请求分发到应用服务器&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;数据库读写分离
    &lt;ul&gt;
      &lt;li&gt;应用服务器在写数据的时候访问主数据库，主数据库通过主从复制更新机制，同步数据到从数据库，应用服务器即可读数据。通常在应用服务器端使用专门数据访问模块，实现读写分离对应用服务器的分离。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;使用反向代理和cdn加速网站响应
    &lt;ul&gt;
      &lt;li&gt;cdn：内容分发网络，部署在距离终端用户最近的网络服务商，用户的网络请求总是先到达他的网络服务商那里，在这里缓存一些网站的静态资源。
反向代理：当用户请求到大数据中心，最先访问反向代理服务器，缓存一些静态资源，无需分发给应用服务器，直接返回数据给用户&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;分布式文件系统和数据库系统&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;使用nosql和搜索引擎
    &lt;ul&gt;
      &lt;li&gt;通过统一数据访问模块访问数据，减轻应用服务器管理多数据源&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;业务拆分
    &lt;ul&gt;
      &lt;li&gt;根据各业务拆分，独立部署，应用之间通过超链接建立关联。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;分布式服务&lt;/li&gt;
  &lt;li&gt;控制业务架构，比控制技术架构有时候更重要，改变业务减小并发量。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;二架构模式&quot;&gt;二、架构模式&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;分层
    &lt;ul&gt;
      &lt;li&gt;将系统在横向维度上切分成几个部分，每个部分负责一部分相对比较单一的职责，然后通过上层对下层的依赖和调用组成一个完整的系统&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;分割
    &lt;ul&gt;
      &lt;li&gt;根据不同的功能和服务进行分割，封装成高内聚低耦合的模块单元&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;分布式
    &lt;ul&gt;
      &lt;li&gt;将不同模块部署在不同服务器上，通过远程调用协同合作。&lt;/li&gt;
      &lt;li&gt;好处：可以使用更多计算机，能够处理更大的并发。&lt;/li&gt;
      &lt;li&gt;坏处：服务调用必须通过网络，对性能有影响；服务器多，宕机的概率越大，可用性降低；数据一致性也困难；管理维护困难。&lt;/li&gt;
      &lt;li&gt;几种分布式：&lt;/li&gt;
      &lt;li&gt;分布式应用和服务：将应用和服务模块分布式部署，改善并发，还可以复用共同服务&lt;/li&gt;
      &lt;li&gt;分布式静态资源：网站静态资源（js，css）独立部署，动静分离，可以减轻应用服务器的负载压力&lt;/li&gt;
      &lt;li&gt;分布式数据和存储，分布式计算&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;三性能优化&quot;&gt;三、性能优化&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;web前端优化
    &lt;ul&gt;
      &lt;li&gt;减少http请求（合并css，js）；&lt;/li&gt;
      &lt;li&gt;使用浏览器缓存（http头cache-control和expires）；&lt;/li&gt;
      &lt;li&gt;启用压缩（服务端进行压缩，浏览器端进行解压缩）&lt;/li&gt;
      &lt;li&gt;css放在页面最上面（浏览器会下载完全部css后对页面进行渲染）&lt;/li&gt;
      &lt;li&gt;减少cookie传输&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;应用服务器性能优化
    &lt;ul&gt;
      &lt;li&gt;缓存：将数据存储在相对较高访问速度的存储介质中。&lt;/li&gt;
      &lt;li&gt;频繁修改的数据，没有热点的访问，数据不一致与脏读，缓存预热，缓存穿透&lt;/li&gt;
      &lt;li&gt;分布式缓存：JBoss Cache为代表的需要更新同步的分布式缓存，memcached为代表的不互相通信的分布式缓存&lt;/li&gt;
      &lt;li&gt;异步操作：在高并发的情况下，直接写入数据库会有压力，引入消息队列，用户请求的数据发给消息队列后立即返回，再由消息队列的消费者进程从消息队列中获取数据，异步写入数据库。（由于异步，要修改业务配合数据不实时性）&lt;/li&gt;
      &lt;li&gt;多线程，注意问题：将对象设计为无状态对象，使用局部对象，并发访问资源时使用锁&lt;/li&gt;
      &lt;li&gt;资源复用：减少开销很大的系统资源的创建和销毁。两种模式：单例和对象池&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;存储性能优化
    &lt;ul&gt;
      &lt;li&gt;机械硬盘：快速顺序读写，慢速随机读写&lt;/li&gt;
      &lt;li&gt;SSD硬盘：没有机械装置，数据存储在记忆的硅晶体&lt;/li&gt;
      &lt;li&gt;为了改善数据访问特性，文件系统或数据库系统会对数据排序后存储，所以很多数据库用b+树，LSM树&lt;/li&gt;
      &lt;li&gt;RAID（廉价磁盘冗余阵列），HDFS（分布式文件系统）&lt;/li&gt;
      &lt;li&gt;RAID0:数据写入磁盘时，分成N份同时写入，不做备份&lt;/li&gt;
      &lt;li&gt;RAID1：数据写入磁盘时，将一份数据同时写入两块磁盘&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;四高可用性&quot;&gt;四、高可用性&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;应用服务器集群的Session管理
    &lt;ul&gt;
      &lt;li&gt;Session复制：每台服务器都保存用户的session信息。只能针对小型的集群&lt;/li&gt;
      &lt;li&gt;Session绑定：负载均衡器总是将来源同一个ip的请求转发给对应的服务器（无法应对服务器宕机）&lt;/li&gt;
      &lt;li&gt;利用cookie记录Session，cookie存在在客户端，不过受cookie大小等影响
Session服务器，独立部署管理Session&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;CAP：数据一致性，数据可用性，分区耐受性
    &lt;ul&gt;
      &lt;li&gt;数据备份：&lt;/li&gt;
      &lt;li&gt;冷备份，定时复制数据，不能保证实时性。&lt;/li&gt;
      &lt;li&gt;热备份：异步热备份和同步热备份&lt;/li&gt;
      &lt;li&gt;失效转移：失效确认（心跳检测，应用程序访问），访问转移，数据恢复&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;自动化测试，Selenium；
    &lt;ul&gt;
      &lt;li&gt;预发布验证（特殊服务器，和正式环境是同一个数据中心）&lt;/li&gt;
      &lt;li&gt;代码控制：svn，（分支开发，主干发布） git&lt;/li&gt;
      &lt;li&gt;自动化发布&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;五架构伸缩性&quot;&gt;五、架构伸缩性&lt;/h2&gt;
&lt;p&gt;（不改变系统软硬件设计，仅仅改变部署的服务器数量，扩大或者缩小服务处理能力）&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;不同功能进行物理分离实现伸缩
    &lt;ul&gt;
      &lt;li&gt;纵向分离：将业务处理流程上的不同部分分离部署（应用服务器，数据服务器，缓存服务器）&lt;/li&gt;
      &lt;li&gt;横向分离：将不同业务模块分离部署（卖家后台，买家后台）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;单一功能通过集群规模实现伸缩
    &lt;ul&gt;
      &lt;li&gt;应用服务器集群伸缩性（负载均衡服务器，http分发），&lt;/li&gt;
      &lt;li&gt;http重定向负载均衡，DNS域名解析负载均衡，反向代理负载均衡，ip负载均衡&lt;/li&gt;
      &lt;li&gt;负载均衡算法：轮询，加权轮询，随机，最少连接，源地址散列&lt;/li&gt;
      &lt;li&gt;分布式缓存集群伸缩性&lt;/li&gt;
      &lt;li&gt;数据存储服务器集群伸缩性&lt;/li&gt;
      &lt;li&gt;数据库读写分离，业务分割数据分库。&lt;/li&gt;
      &lt;li&gt;（Cobar：分布式数据访问代理）开源的分库插件&lt;/li&gt;
      &lt;li&gt;TDDL主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;六架构的扩展性&quot;&gt;六、架构的扩展性&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;利用分布式消息队列降低系统耦合性
    &lt;ul&gt;
      &lt;li&gt;事件驱动架构：通过在低耦合模块之间传输事件消息，并借助事件消息完成模块之间的合作。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;利用分布式服务打造可复用的业务平台&lt;/li&gt;
  &lt;li&gt;分布式服务框架
    &lt;ul&gt;
      &lt;li&gt;Dubbo:开源分布式服务框架&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;七架构的安全性&quot;&gt;七、架构的安全性&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;XSS攻击（跨站点脚本攻击）
    &lt;ul&gt;
      &lt;li&gt;反射型攻击：攻击者诱使用户点击一个嵌入恶意脚本的链接，达到攻击的目的&lt;/li&gt;
      &lt;li&gt;持久性攻击：提交恶意脚本的请求，保存在被攻击web站点，用户浏览就被攻击。&lt;/li&gt;
      &lt;li&gt;防范：&lt;/li&gt;
      &lt;li&gt;消毒：对特殊符号（&amp;lt;&amp;gt;=等）进行转义&lt;/li&gt;
      &lt;li&gt;HttpOnly：浏览器禁止页面js访问带有httpOnly属性的cookie&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;注入攻击
    &lt;ul&gt;
      &lt;li&gt;SQL注入：在http请求中恶意注入sql命令，服务器用请求参数构造sql命令，就执行了恶意的sql&lt;/li&gt;
      &lt;li&gt;防范：&lt;/li&gt;
      &lt;li&gt;消毒：通过正则表达式过滤特殊命令&lt;/li&gt;
      &lt;li&gt;参数绑定：使用预编译手段，绑定参数&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;CSRF攻击：跨站点请求伪造，以合法用户进行非法操作(在第三方网站直接使用img等标签嵌入攻击的网站，会自动带入cookie信息模拟登陆)
    &lt;ul&gt;
      &lt;li&gt;防范：&lt;/li&gt;
      &lt;li&gt;表单token：在表单提交的时候加入token随机数&lt;/li&gt;
      &lt;li&gt;验证码：在支付交易等关键页面加入验证码&lt;/li&gt;
      &lt;li&gt;Referer check：检查http请求头的referer域中的请求来源&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ModSecurity开源的web应用防火墙
    &lt;ul&gt;
      &lt;li&gt;采用处理逻辑与攻击规则集合分离的架构模式&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;信息加密
    &lt;ul&gt;
      &lt;li&gt;单向散列加密：(MD5,SHA)通过对不同输入长度的信息进行散列计算，得到固定长度输出，可以加强加入salt（密钥），用彩虹表破解（常用密码和密文对应关系）&lt;/li&gt;
      &lt;li&gt;对称加密:（DES，RC算法）加密和解密使用的密钥是同一个密钥。如Cookie加密，通信加密&lt;/li&gt;
      &lt;li&gt;非对称加密：（RSA算法）加密和解密使用的密钥不相同，对外界公开的叫公钥，只有所有者知道的叫私钥。如：信息安全传输，数字签名。https传输中浏览器用的数字证书&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;信息过滤和反垃圾
    &lt;ul&gt;
      &lt;li&gt;文本匹配：解决敏感词过滤的问题。内容短的时候可以使用正则表达式，内容多的情况可以使用trie树，双数组trie算法。对做手脚的信息做降噪预处理，然后在匹配。&lt;/li&gt;
      &lt;li&gt;分类算法：（ARCS算法，贝叶斯分类算法）先将批量已分类的邮件样本输入分类算法，得到分类模型，再利用模型进行识别&lt;/li&gt;
      &lt;li&gt;黑名单：被标记为垃圾的加入黑名单，还可以用于信息去重。数据量过大可以考虑布隆过滤器代替hash表&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Mon, 16 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/2019/12/16/%E5%A4%A7%E5%9E%8B%E7%BD%91%E7%AB%99%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/2019/12/16/%E5%A4%A7%E5%9E%8B%E7%BD%91%E7%AB%99%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</guid>
        
        <category>读书笔记</category>
        
        
        <category>读书笔记</category>
        
      </item>
    
      <item>
        <title>Redis深度历险读书笔记</title>
        <description>&lt;h2 id=&quot;一基础结构&quot;&gt;一、基础结构&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;redis字符串：扩容，在1M内扩容都是加倍，超过就是1M，最大长度512M&lt;/li&gt;
  &lt;li&gt;整数的范围：signed long 的最大最小值，超过了这个值，Redis 会报错&lt;/li&gt;
  &lt;li&gt;hash:渐进式 rehash 会在 rehash 的同时，保留新旧两个 hash结构，查询时会同时查询两个 hash 结 构，然后在后续的定时任务中以及 hash 操作指令中，循序渐进地将旧 hash 的内容一点点迁移到 新的 hash 结构中&lt;/li&gt;
  &lt;li&gt;set 结构可以用来存储活动中奖的用户ID，因为有去重功能，可以保证同一个用户不会中奖两次&lt;/li&gt;
  &lt;li&gt;zset 可以用来存粉丝列表，value 值是粉丝的用户 ID，score 是关注时间。我们可以对粉丝列表按 关注时间进行排序&lt;/li&gt;
  &lt;li&gt;容器型数据结构,如果容器里元素没有了，那么立即删除元素，释放内存&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;二架构&quot;&gt;二、架构&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;哨兵模式
    &lt;ul&gt;
      &lt;li&gt;启动哨兵：Sentinel端口：26379，返回主从redis地址，监控主从节点是否正常，客观，主观下线节点
&lt;a href=&quot;https://groups.google.com/forum/#!topic/redis-db/0-VTdPlC8bI&quot;&gt;辅助&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;codis：redis集群中间件
    &lt;ul&gt;
      &lt;li&gt;中间代理：将key做hash运算，划分1024个槽位&lt;/li&gt;
      &lt;li&gt;缺点：不支持事务，rename无法正确操作&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/wengwh/learn-notes/master/youdao/clipboard.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;集群模式Cluster
    &lt;ul&gt;
      &lt;li&gt;相对于 Codis 的不同，它是去中心化的，如图所示，该集群有三个 Redis 节点组成，每个节点负 责整个集群的一部分数据，每个节点负责的数据多少可能不一样。这三个节点相互连接组成一个对 等的集群，它们之间通过一种特殊的二进制协议相互交互集群信息&lt;/li&gt;
      &lt;li&gt;Redis Cluster 将所有数据划分为 16384 的 slots，它比 Codis 的 1024 个槽划分的更为精细，每个节点负责其中一部分槽位。槽位的信息存储于每个节点中，它不像 Codis，它不需要另外的分布式 存储来存储节点槽位信息。 当 Redis Cluster 的客户端来连接集群时，它也会得到一份集群的槽位配置信息。这样当客户端要 查找某个 key 时，可以直接定位到目标节点&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;三使用场景&quot;&gt;三、使用场景&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;记录帖子的点赞数、评论数和点击数 (hash)&lt;/li&gt;
  &lt;li&gt;记录用户的帖子ID 列表 (排序)，便于快速显示用户的帖子列表 (zset)&lt;/li&gt;
  &lt;li&gt;记录帖子的标题、摘要、作者和封面信息，用于列表页展示 (hash)&lt;/li&gt;
  &lt;li&gt;记录帖子的点赞用户 ID 列表，评论 ID 列表，用于显示和去重计数 (zset)&lt;/li&gt;
  &lt;li&gt;缓存近期热帖内容 (帖子内容空间占用较大)，减少数据库压力 (hash)&lt;/li&gt;
  &lt;li&gt;记录帖子的相关文章 ID，根据内容推荐相关帖子(list)&lt;/li&gt;
  &lt;li&gt;如果帖子ID 是整数自增的，可以使用 Redis 来分配帖子ID(计数器)&lt;/li&gt;
  &lt;li&gt;收藏集和帖子之间的关系 (zset)&lt;/li&gt;
  &lt;li&gt;记录热榜帖子ID 列表，总热榜和分类热榜 (zset)&lt;/li&gt;
  &lt;li&gt;缓存用户行为历史，进行恶意行为过滤 (zset,hash)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;四应用&quot;&gt;四、应用&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;分布式锁：set lock:codehole true nx px 5000（存在业务时间超过锁的时间，redis宕机风险）&lt;/li&gt;
  &lt;li&gt;延时队列：使用list数据结构做异步消息队列使用，使用rpush/lpush 操作入队列，使用lpop 和 rpop 来出队列，（可以考虑使用阻塞读：blpop/brpop）（无法保证ack，队列消息被读出之后，业务异常了，消息就丢失）&lt;/li&gt;
  &lt;li&gt;位图：setbit、getbit、bitcount：减少存储空间(实际也是字符串，使用位做1，0存储，会自动扩容)（比如：记录用户365天签到情况）。bitfield：一次操作多个位&lt;/li&gt;
  &lt;li&gt;HyperLogLog：不精准的去重统计（网页uv）指令：pfadd，pfcount，pfmerge。会占据12k的存储空间&lt;/li&gt;
  &lt;li&gt;Bloom Filter：不精准的contains判断，插件式安装，指令：bf.add 添加元素， bf.exists 查询元素是否存在&lt;/li&gt;
  &lt;li&gt;redis-cell：令牌桶限流，指令：cl.throttle laoqian:reply 15 30 60 1，（key，容量，令牌个数，时间，默认参数1）&lt;/li&gt;
  &lt;li&gt;GeoHash：地理位置，
指令：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;添加一个坐标：geoadd company 116.48105 39.996794 juejin
计算2个元素的距离：geodist company juejin ireader km
获取元素的经纬度：geopos company juejin
获取元素的hahs值：geohash company ireader
获取范围内附近的其他元素升序（会包括本身）：georadiusbymember company ireader 20 km count 3 asc
根据坐标值来查询附近的元素：georadius company 116.514202 39.905409 20 km withdist count 3 asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;scan：scan 0 match key99* count 1000 （cursor 整数值（第一次0），key 的正则模式，遍历的 limit hint（不是返回数量））&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;五原理&quot;&gt;五、原理&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;线程 IO 模型：Redis 是个单线程程序，使用多路复用NIO&lt;/li&gt;
  &lt;li&gt;通信协议：RESP 是 Redis 序列化协议的简写(冗余字符串)。它是一种直观的文本协议，优势在于实现异常简单，解析性能 极好&lt;/li&gt;
  &lt;li&gt;持久化：第一种是快照，第二种是 AOF日志
    &lt;ul&gt;
      &lt;li&gt;3.1. Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程，快照持久化完全交给子进程来处理，父进程继续处理客户端请求&lt;/li&gt;
      &lt;li&gt;3.2. AOF 日志存储的是 Redis 服务器的顺序指令序列，AOF 日志只记录对内存进行修改的指令记录。AOF重写：其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令，序列化到一个新的 AOF 日志文件中。fsync：当程序对 AOF日志文件进行写操作时，实际上是将内容写到了 内核为文件描述符分配的一个内存缓存中，然后内核会异步将脏数据刷回到磁盘的。可以设置每隔1S左右执行fsync&lt;/li&gt;
      &lt;li&gt;3.3. Redis 4.0 混合持久化，先从快照恢复数据，在执行增量的aof日志&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;管道：客服端实现，把指令合并，先写入缓冲区，在一起发送到服务端，在读取返回的结果&lt;/li&gt;
  &lt;li&gt;事务：分别是 multi/exec/discard。multi 指示事务的开始，exec 指示 事务的执行，discard 指示事务的丢弃。
    &lt;ul&gt;
      &lt;li&gt;5.1. Redis 提供了这种 watch 的机制，它就是一种乐观锁&lt;/li&gt;
      &lt;li&gt;5.2. 如果是由于语法错误或者error错误，那么整体会回滚（失效）； 如果是由于参数类型错误，语法没有错误，那么整体中的部分会有效，其他会失效。（无法保证原子性）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;pubsub：发布订阅（无法持久化，独立项目：Disque）&lt;/li&gt;
  &lt;li&gt;小对象压缩：会动态变化（object encoding key）
    &lt;ul&gt;
      &lt;li&gt;7.1. ziplist：紧凑的字节数组结构，当list或者hash的个数小于512并且任意元素长度小于64，&lt;/li&gt;
      &lt;li&gt;7.2. intset：紧凑的整数数组结构，当zset个数128长度小于64，set个数小于512&lt;/li&gt;
      &lt;li&gt;7.3. 内存分配算法：使用第三方库：jemalloc(facebook)库（默认），tcmalloc(google)。查看info memory&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;主从复制（CAP）：网络分区发生时，一致性和可用性两难全
    &lt;ul&gt;
      &lt;li&gt;8.1. 增量同步：主节点记录指令到内存buffer（环形数组），如果满了，就头部覆盖前面的内容，如果网络不好，会出现指令丢失&lt;/li&gt;
      &lt;li&gt;8.2. 快照同步：非常耗资源，主库进行bgsave保存快照到磁盘进行同步从节点，从节点进行全量加载，同步过程，主节点buffer还会记录，如果太小，导致增量无法完成，还会触发快照同步，死循环（配置合适的buffer大小）&lt;/li&gt;
      &lt;li&gt;8.3. 新增从节点到集群，先进行一次快照同步，完成后再进行增量同步&lt;/li&gt;
      &lt;li&gt;8.4. 无盘复制：主节点一遍遍历生成快照，一边将内容发送到从节点，从节点存储到磁盘文件中，再进行一次性加载&lt;/li&gt;
      &lt;li&gt;8.5. wait指令：wait 从库数量 等待时间ms （wait 1 0），会等待N个从库同步完成&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;六拓展&quot;&gt;六、拓展&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;stream：redis5.0新增的消息队列（仿造kafka），弥补了pub/sub不能持久化消息的缺陷，不同于kafka，不能分区&lt;/li&gt;
  &lt;li&gt;info指令：获取redis所有信息，可以按块获取（info memory）&lt;/li&gt;
  &lt;li&gt;分布式锁：在主从集群，加锁之后出现宕机切换，会出现重复加锁，在集群模式下，redlock算法，对一半以上实例进行加锁&lt;/li&gt;
  &lt;li&gt;过期策略：redis对key的过期检查和删除，维护了一个key的过期时间字典
    &lt;ul&gt;
      &lt;li&gt;4.1. 定时扫描策略：每秒10次进行过期扫描，从字典随机20个key，判断是否过期，超过1/4，继续重复步骤，不过有时间上限：25ms&lt;/li&gt;
      &lt;li&gt;4.2. 惰性策略：客服端访问key的时候，会进行过期时间判断&lt;/li&gt;
      &lt;li&gt;4.3. 从库过期策略：不会自动删除，需要从库删除的时候，del指令同步过去才会删除&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;LRU：内存超过物理内存限制时，会和磁盘产生交换，性能急剧下滑，可以设置maxmemory，超过的时候根据策略来腾出空间
    &lt;ul&gt;
      &lt;li&gt;noeviction（默认）：不做写请求，直接报错&lt;/li&gt;
      &lt;li&gt;volatile-lru：从已设置过期时间的数据集（server.db[i].expires）中挑选最近最少使用的数据淘汰&lt;/li&gt;
      &lt;li&gt;volatile-ttl： 从已设置过期时间的数据集（server.db[i].expires）中挑选将要过期的数据淘汰&lt;/li&gt;
      &lt;li&gt;volatile-random：从已设置过期时间的数据集（server.db[i].expires）中任意选择数据 淘汰&lt;/li&gt;
      &lt;li&gt;allkeys-lru：从数据集（server.db[i].dict）中挑选最近最少使用的数据淘汰&lt;/li&gt;
      &lt;li&gt;allkeys-random：从数据集（server.db[i].dict）中任意选择数据淘汰
LRU实现：每个key增加一个小字段保存时间戳，随机获取5（maxmemory_samples）个key，淘汰最旧的，如果内存还是不够，再来一次。3.0版本加入了淘汰池，淘汰结束的时候，会保留最旧的5个放到池里面，下次再随机5个跟池里面的融合淘汰&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;懒惰删除：不止有一个主线程，还有几个异步线程专门处理耗时的操作，包括删除，同步，LRU淘汰，过期key等
    &lt;ul&gt;
      &lt;li&gt;比如：4.0版本: 异步删除：unlink（旧版:del） 、flushall async&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;redis安全
指令改名：rename-command keys abckeysabc、禁用：rename-command flushall “”
    &lt;ul&gt;
      &lt;li&gt;SSL代理：（本身不支持）官方推荐：spiped&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;七源码&quot;&gt;七、源码&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;字符串内部结构
    &lt;ul&gt;
      &lt;li&gt;1.1. SDS，带长度信息的字节数组，类似java的ArrayList&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct SDS&amp;lt;T&amp;gt;{
 T capacity://数组容量
 T len;//数组长度
 byte flags;//特殊标示位
 byte[] content;//数组内容
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;1.2. 在长度特别短时，使用 emb 形式存储 (embeded)，当长度超过 44 时，使用 raw 形式存储&lt;/li&gt;
  &lt;li&gt;1.3. 扩容策略：长度小于1M之前，扩容采用加倍，超过之后，每次增加1M&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;字典内部结构
    &lt;ul&gt;
      &lt;li&gt;2.1. dict 是 Redis 服务器中出现最为频繁的复合型数据结构，除了 hash 结构的数据会用到字典外，整个 Redis 数据库的所有 key 和 value 也组成了一个全局字典，还有带过期时间的 key 集合也是一个字典。 zset 集合中存储 value 和 score 值的映射关系也是通过 dict 结构实现&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct zset {
    dict *dict; // all values value=&amp;gt;score
    zskiplist *zsl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;2.2. dict 结构内部包含两个hashtable，日常使用其中一个，宁外一个使用在扩容的时候&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct dict {
    ......
    dictht ht[2];
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;2.3. hashtable结构等同于java的hashmap，也是用链地址解决hash冲突&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct dictEntry {
  void* key;
  void* val;
  dictEntry* next; // 链接下一个 entry
}

struct dictht {
  dictEntry** table; // 二维
  long size; // 第一维数组的长度
  long used; // hash 表中的元素个数
  ......
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;2.4. 渐进式rehash：在扩容的时候，不是一次性把全部的键值都rehash到新的hashtable，将 rehash 键值对所需的计算工作均滩到对字典的每个添加、删除、查找和更新操作上， 从而避免了集中式 rehash 而带来的庞大计算量，同时会启动定时器做rehash
在字典中维持一个索引计数器变量 rehashidx，来记录rehash到第几个索引，全部结束-1
在rehash期间，字典的删除查找更新都会在2个hashtable进行，新增会直接更新到新的hashtable&lt;/li&gt;
  &lt;li&gt;2.5. hash函数：siphash&lt;/li&gt;
  &lt;li&gt;2.6. hash攻击：利用hash函数的偏向性，对服务器攻击，导致所有元素都到hash冲突的链表中，导致查询速度变成o(n)&lt;/li&gt;
  &lt;li&gt;2.7. 扩容条件：hash表中元素等于数组长度时扩容，如果redis在做bgsave，会不去扩容，如果元素个数达到5倍，会强制扩容&lt;/li&gt;
  &lt;li&gt;2.8. 缩容条件：当元素个数低于数组长度的10%，会进行缩容&lt;/li&gt;
  &lt;li&gt;2.9. set 的结构：底层实现也是字典，只不过所有的 value 都是 NULL，其它的特性和字典一模一样&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;压缩列表ziplist：
    &lt;ul&gt;
      &lt;li&gt;3.1. zset 和 hash 容器对象在元素个数较少的时候，采用压缩列表 (ziplist) 进行存储&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct ziplist&amp;lt;T&amp;gt; {
  int32 zlbytes; // 整个压缩列表占用字节数
  int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量，用于快速定位到最后一个节点
  int16 zllength; // 元素个数
  T[] entries; // 元素内容列表，挨个挨个紧凑存储
  int8 zlend; // 标志压缩列表的结束，值恒为 0xFF
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;3.2. prevlen 字段表示前一个 entry 的字节长度。当字符串长度小于254(0xFE) 时，使用一个字节表示，如果达到或超出 254(0xFE) 那就使用 5 个字节来表示&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct entry {
  int&amp;lt;var&amp;gt; prevlen; // 前一个 entry 的字节长度
  int&amp;lt;var&amp;gt; encoding; // 元素类型编码
  optional byte[] content; // 元素内容
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;3.3. ziplist紧凑存储，没有冗余空间，插入新元素，就调用realloc 扩展内存，需要进行内容拷贝（看内存分配）。如果ziplist占据内存太大，重新分配和拷贝内容都很大消耗。所以不适合大型数据&lt;/li&gt;
  &lt;li&gt;3.4. 级联更新：如果某个元素字节从小于254更新到大于，下一个元素的prevlen，从1字节扩大到5字节，如果这个元素本身也正好是253字节，就会导致它的下一个元素也更新&lt;/li&gt;
  &lt;li&gt;3.5. intset：当 set 集合容纳的元素都是整数并且元素个数较小时， Redis 会使用 intset 来存储结合元素。 intset 是紧凑的数组结构，同时支持 16 位、 32 位和 64 位整数&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct intset&amp;lt;T&amp;gt; {
    int32 encoding; // 决定整数位宽是 16 位、 32 位还是 64 位
    int32 length; // 元素个数
    int&amp;lt;T&amp;gt; contents; // 整数数组，可以是 16 位、 32 位和 64 位
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;快速列表（quicklist）
quicklist 是 ziplist 和 linkedlist 的混合体，它将 linkedlist 按段切分，每一段使用 ziplist 来紧凑存储，多个 ziplist 之间使用双向指针串接起来，为了进一步节约空间， 会对ziplist 进行压缩存储，使用 LZF 算法压缩，可以选择压缩深度。
比如存储12个元素，ziplist存储3个，就会存在4个节点，每个节点的ziplist3个元素&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;list-max-ziplist-size：ziplist长度，默认8字节
listcompress-depth：压缩深度，首尾2个元素不压缩，深度1，以此类推。默认0
struct quicklist {
  quicklistNode* head;
  quicklistNode* tail;
  long count; // 元素总数
  int nodes; // ziplist 节点的个数
  int compressDepth; // LZF 算法压缩深度
  ...
}

struct quicklistNode {
  quicklistNode* prev;
  quicklistNode* next;
  ziplist* zl; // 指向压缩列表
  int32 size; // ziplist 的字节总数
  int16 count; // ziplist 中的元素数量
  int2 encoding; // 存储形式 2bit，原生字节数组还是 LZF 压缩存储
  ...
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;跳跃列表（skiplist）
    &lt;ul&gt;
      &lt;li&gt;5.1. zset存储score做有序查找，正常按链表存储score，查找的复杂度就是o(n),如果把某些节点变成多层，查找就变成顶层遍历，找到大于元素的节点，退回上节点，在下一层继续查找，复杂度O(lg(n))&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct zsl {
    zslnode* header; // 跳跃列表头指针
    int maxLevel; // 跳跃列表当前的最高层
    map&amp;lt;string, zslnode*&amp;gt; ht; // hash 结构的所有键值对
}

struct zslnode {
    string value;
    double score;
    zslnode*[] forwards; // 多层连接指针（zslforward*[] forwards; ）
    zslnode* backward; // 回溯指针
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;5.2. 每新增节点都要随机计算层数，期望是50%，在代码中晋升概率只有 25%，因为层数一般不高，在顶层往下遍历会浪费，记录maxLevel，遍历时直接从maxLevel开始，提高很多性能&lt;/li&gt;
  &lt;li&gt;5.3. score修改，直接删除再插入&lt;/li&gt;
  &lt;li&gt;5.4. score值一样：会比较value值（字符串比较）&lt;/li&gt;
  &lt;li&gt;5.5. 元素排名：在skiplist的forward指针上增加了span跨度属性，前一个节点到当前节点会跳过多少个节点，rank搜索路径的span累加&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct zslforward {
    zslnode* item;
    long span; // 跨度
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;紧凑列表（listpack）redis5.0引入
    &lt;ul&gt;
      &lt;li&gt;6.1. 对ziplist结构的改进，在存储空间上会更节省，也更精简，删掉了zltail_offset字段，可以通过total_bytes 字段和最后一个元素的长度字段计算&lt;/li&gt;
      &lt;li&gt;6.2. 长度字段使用 varint 进行编码，不同于 skiplist 元素长度的编码为 1 个字节或者 5 个字节， listpack 元素长度的编码可以是 1、 2、 3、 4、 5 个字节。同 UTF8 编码一样，它通过字节的最高为是否为 1 来决定编码的长度&lt;/li&gt;
      &lt;li&gt;6.3. 结构&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct listpack&amp;lt;T&amp;gt; {
    int32 total_bytes; // 占用的总字节数
    int16 size; // 元素个数
    T[] entries; // 紧凑排列的元素列表
    int8 end; // 同 zlend 一样，恒为 0xFF
}

struct lpentry {
    int&amp;lt;var&amp;gt; encoding;
    optional byte[] content;
    int&amp;lt;var&amp;gt; length;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;6.4. 级联更新就不存在，元素之间的独立的，目前只有stream数据结构使用了listpack&lt;/li&gt;
  &lt;li&gt;6.5. 比对总结：ziplist设计问题，正向遍历可以，逆向遍历，无法直接定位到元素的开始，所以多了total_bytes,prevlen字段，listpack，逆向遍历，可以直接识别到长度，就定位到了元素位置。&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;Rax(基数树 Radix Tree)
    &lt;ul&gt;
      &lt;li&gt;7.1. 按照 key 的字典序排列，支持快速地定位、插入和删除操作&lt;/li&gt;
      &lt;li&gt;7.2. 用在 Redis Stream 结构里面用于存储消息队列，在 Stream 里面消息 ID 的前缀是时间戳 + 序号，这样的消息可以理解为时间序列消息&lt;/li&gt;
      &lt;li&gt;7.3. 结构：rax 中有非常多的节点，根节点、叶节点和中间节点，有些中间节点带有 value，有些中间节点纯粹是结构性需要没有对应的 value&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct raxNode {
    int&amp;lt;1&amp;gt; isKey; // 是否没有 key，没有 key 的是根节点
    int&amp;lt;1&amp;gt; isNull; // 是否没有对应的 value，无意义的中间节点
    int&amp;lt;1&amp;gt; isCompressed; // 是否压缩存储，这个压缩的概念比较特别
    int&amp;lt;29&amp;gt; size; // 子节点的数量或者是压缩字符串的长度 (isCompressed)
    byte[] data; // 路由键、子节点指针、 value 都在这里
}
压缩结构
struct data {
  optional struct { // 取决于 header 的 size 字段是否为零
   byte[] childKey; // 路由键
   raxNode* childNode; // 子节点指针
  } child;
  optional string value; // 取决于 header 的 isNull 字段
}

非压缩节点
struct data {
    byte[] childKeys; // 路由键字符列表
    raxNode*[] childNodes; // 多个子节点指针
    optional string value; // 取决于 header 的 isNull 字段
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/wengwh/learn-notes/master/youdao/tree.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结：&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;hash，set：存储根据数据量和大小判断使用ziplist还是dict结构&lt;/li&gt;
  &lt;li&gt;set：数据量都是整数同时个数少的时候用intset&lt;/li&gt;
  &lt;li&gt;zset：同时需要使用skiplist存储score&lt;/li&gt;
  &lt;li&gt;list：使用quicklist存储&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 13 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/2019/12/13/Redis%E6%B7%B1%E5%BA%A6%E5%8E%86%E9%99%A9%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/2019/12/13/Redis%E6%B7%B1%E5%BA%A6%E5%8E%86%E9%99%A9%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</guid>
        
        <category>读书笔记</category>
        
        
        <category>读书笔记</category>
        
      </item>
    
      <item>
        <title>2018年终感悟</title>
        <description>&lt;p&gt;2018年过完了，在过完年，准备上班的晚上开始写去年的年终总结。2018年过完年已经到2月底，由于去年封闭开发，公司给我们多放了几天假期。由于准备好了离职，就提前来城市，准备找工作面试的事情。&lt;/p&gt;

&lt;p&gt;3月，开始了骑驴找马的面试，一开始2家公司面试，都没问什么技术，也不肯给我更高的工资，自己有点受打击。感觉这么快就被社会淘汰了吗？再没找工作之前以为自己应该很好找到新工作，面试了2家之后自信心受到了打击。然后就开始了刷题，准备各种知识点。&lt;/p&gt;

&lt;p&gt;4月，面试了互联网公司，开的工资他们直接同意了，不过是大小周工作制加最低五险一金，自己有点犹豫。后来去了原来同事的公司，做游戏运营开发，虽然没给我期望的工资，给的岗位也只是中级，当时自己也找不到更好的，就同意了offer。然后就开始提离职走交接。中间也帮公司面试了一下应聘的人，渐渐发现工作年龄并不是特别大的用处，有时候做了好几年接触的面也很窄，对个人的提升也没有很大。&lt;/p&gt;

&lt;p&gt;5月，入职新公司，虽然面试的岗位是大数据开发岗，不过做的工作也都是跟以前内容一样做业务接口相关的。对于自己而言难度并不大。一开始安排我做礼包系统，涉及游戏的礼包触发，逻辑规则判断是否触发等相关业务。第一个月主要是在适应公司，融入团队，不过新的公司，同事之间的氛围相对上家公司确实好了很多，同事之间不会很拘谨，公司的团建也很多，感觉公司对这块也比较重视，给我感觉还是蛮好的。&lt;/p&gt;

&lt;p&gt;6-7月，接手之前同事做的排行榜接口模块，领导想统一接口都我这边开发。第二个接手，接口网关模块，由于之前的框架使用了dubbo，网关一开始设计是每个服务打jar包给网关写代码调用。感觉之前的项目代码都不像是生产正式版本，在很多公共包还存在测试的代码，公司可能也成立没非常久，对代码规范，流程等都没有做好建设。&lt;/p&gt;

&lt;p&gt;自己查看了网络相关的网关功能，由于内部使用了dubbo，不考虑springcloud集成zuul，不过可以模仿zuul做相关功能。网关使用dubbo泛化调用解决了打包的硬编码，加入了现在网关都具备的功能：接口鉴权，黑白名单过滤，限流控制，熔断降级等。编码过程参考zuul的源码，一样的使用拦截器，使用hystrix插件，令牌桶算法等相关技术。&lt;/p&gt;

&lt;p&gt;8月，礼包系统上线之前的压测，使用了jmeter做并发压测，不过对jmeter的使用只停留在以前简单配置并发。缺少考虑了压测机的性能，以及部署程序机的性能，感觉做的压测都不是太大作用。不过还是发现了一些问题，比如第三方jar解析缓慢，导致堆积等问题。&lt;/p&gt;

&lt;p&gt;9月，辅助一个模块的同事，写入es性能缓慢，由于es的主键冲突问题，考虑了hbase的协处理器和kafka两种方式做一个主键拆分。由于协处理器需要在hbase上面部署，感觉可能会对hbase产生不确定影响，选择了kafka做主键区分。先写入kafka，再监听相关管道，消费写入es。&lt;/p&gt;

&lt;p&gt;后续还考虑了自动化部署和性能监控，了解了jenkins和apm。jenkins自己搭了一个开发环境，不过由于生产的复杂和权限问题，没有推动。apm查看了现在几个主流的组件，基本都是使用字节码嵌入，达到最少干扰项目的做法，不过公司的项目也都是刚上生产，apm的隐藏忧患也没有正式研究，只暂停在个人研究阶段。&lt;/p&gt;

&lt;p&gt;10-11月，礼包生产上线成功之后，后续的新的需求继续开发，由于都不是复杂的业务，相对过程都比较顺滑。后续又开始做广告的花费采集，采集公司投放的几个大渠道的花费。通过他们提供的api抓取花费等数据写入hive。广告api的研究主要工作在各大厂家的api查看，不过都大同小异，就是每个api都有一些自己的限制等。编码上问题都不是很大。中间还接触了一下定时组件airflow，使用它定时触发我的程序来抓取api。第三方的定时组件，功能丰富，而且直观，使用python编写脚本，还是比较强大。&lt;/p&gt;

&lt;p&gt;12月，主要的事情就是把手头上相关的新需求做完上线。还接手了一些业务接口。&lt;/p&gt;

&lt;p&gt;在新公司，接触到的组件相对较多，es，kafka，apollo，hbase，hdfs等，自己在使用上问题不大，不过今年没有花时间去研究他们的内部原理等。只是停留在使用层面上。希望新的一年可以研究一下。&lt;/p&gt;

&lt;p&gt;最后说说这一年的感情生活，今年终于有了女朋友，虽然出现了一些小问题，不过不影响我们的发展，在新的一年也会结婚。有了女朋友，周末也不用一个人宅着，人也有了期望，虽然有时候感觉压力大，但是有了目标，生活也相对踏实。这应该是这一年最大的收获了。&lt;/p&gt;

&lt;p&gt;今年自己博客基本没更新，也没怎么看书，在知识这块感觉投入太少了。开源的项目plumdo-work居然有了110多的star和70多fork对自己是个意外，虽然我一直想把他基础做完，但是自己实在懒，都是做一点点，导致一年了，就多了一些功能，还缺很多，不过有了关注，自己也更有动力去完善，只能期望新的一年可以完成基础版。&lt;/p&gt;

&lt;p&gt;再说说自己的投资，今年还是一如既往的股票，不过到了新公司，工作内容以及适应，没有太多时间去玩股票，有了女朋友也不想再投钱进去，希望存点钱为后续结婚等事情准备，之前进去的钱，就看后续的走势了。虽然大家都不看好股市，不过好歹是一种投资。虽然一直没赚钱，当作自己在理财这块唯一的爱好了。&lt;/p&gt;

&lt;p&gt;对明年的期望，完善开源项目，对使用到的组件等有一些深入研究，当然期望自己在岗位上可以有更多的发展，毕竟到了30岁，也要考虑以后的职业发展了。对感情，新的一年与女友结婚，稳定的步入婚姻生活，过好两个人的生活。&lt;/p&gt;

</description>
        <pubDate>Mon, 11 Feb 2019 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2019/02/11/2018%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2019/02/11/2018%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</guid>
        
        <category>工作</category>
        
        
        <category>工作</category>
        
      </item>
    
      <item>
        <title>深入JVM读书笔记</title>
        <description>&lt;p&gt;今年买了本深入java虚拟机的书来学习，由于工作时间，只读了一半，争取后续读完，顺便贴一下自己的读书笔记。&lt;/p&gt;

&lt;h4 id=&quot;一jvm介绍&quot;&gt;一、JVM介绍&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Classic VM：第一代虚拟机
Exact VM：生命周期短
Sun HotSpot VM：目前最广的虚拟机
JRockit VM：BEA公司开发，速度快（后期与HotSpot融合）
Dalvik VM：Android平台的虚拟机（没有遵循虚拟机规范）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;二内存管理&quot;&gt;二、内存管理&lt;/h4&gt;
&lt;h5 id=&quot;1java内存区域&quot;&gt;1.JAVA内存区域&lt;/h5&gt;

&lt;p&gt;1.1.程序计数器：记录线程所执行的字节码的行号指示器。（对于native方法为空）&lt;/p&gt;

&lt;p&gt;1.2.Java虚拟机栈：方法执行创建栈帧（局部变量表（编译期间就知道分配多大内存），操作数栈，动态链接，方法出口等）用于入栈出栈，局部变量表：存放基本数据类型，对象引用。(long和double占用2个局部变量空间)。如果栈深度大于虚拟机运行的深度，StackOverflowError,无法申请足够的内存OutOfMemoryError(线程私有)&lt;/p&gt;

&lt;p&gt;1.3.本地方法栈：与JAVA虚拟机栈相似，为了执行native方法服务。&lt;/p&gt;

&lt;p&gt;1.4.Java堆：线程共享，虚拟机启动时创建，存放对象实例和数组（类的成员变量）（堆还能细分：新生代，老年代等），堆没有内存可扩展OutOfMemoryError&lt;/p&gt;

&lt;p&gt;1.5.方法区：线程共享，存储被虚拟机加载的类信息， 常量，静态变量，即时编译器编译后的代码等（方法区：垃圾回收低，但也是有回收的必要）OutOfMemoryError（线程共享）&lt;/p&gt;

&lt;p&gt;1.6.运行时常量池：方法区的一部分，Class文件有常量池：存放编译期生成的各种字面量和符号引用，类加载后进入方法区，这部分存放运行时常量池，运行期间也可能将新的常量放入池中，String.intern()，也会抛出OutOfMemoryError&lt;/p&gt;

&lt;p&gt;1.7.直接内存：不是虚拟机规范的内存区域，在NIO中使用native方法直接分配堆外内存，虽然不受虚拟机限制，但是可能超过本机内存，可能会出现OutOfMemoryError&lt;/p&gt;

&lt;h5 id=&quot;2对象创建过程&quot;&gt;2.对象创建过程&lt;/h5&gt;
&lt;p&gt;new指令：检查常量池中定为到一个类的符号引用，并且检查是否被加载、解析和初始化。没有就执行类加载过程，为新生对象分配内存，将分配到的内存空间都初始化为零值，堆对象进行设置（哪个类的实例，哈希码等），执行init方法。&lt;/p&gt;

&lt;p&gt;堆的分配：&lt;/p&gt;

&lt;p&gt;指针碰撞：堆是规整的情况下，只需要堆上的指针移动相对应大小的距离就是分配内存&lt;/p&gt;

&lt;p&gt;空闲列表：堆是不规则的情况，从列表中找到足够大的空间划分给对象实例。&lt;/p&gt;

&lt;p&gt;堆是否规整取决于垃圾收集器是否有压缩整理功能&lt;/p&gt;

&lt;p&gt;内存分配对于线程同步情况：CAS配上失败重试保证原子性，宁外一种每个线程在堆中预先分配一小块内存（本地线程分配缓冲TLAB）只有TLAB用完之后分配新的才需要同步锁定&lt;/p&gt;

&lt;h5 id=&quot;3对象的访问定为&quot;&gt;3.对象的访问定为&lt;/h5&gt;
&lt;p&gt;1.句柄访问：堆中划分一块内存做句柄池，栈上存放句柄地址&lt;/p&gt;

&lt;p&gt;2.直接指针访问：需要考虑类型数据的相关信息，存储对象的地址
句柄的好处：对象移动只需要修改句柄，直接指针定位快（HotSpot用第2种）&lt;/p&gt;

&lt;p&gt;3.内存溢出异常&lt;/p&gt;

&lt;p&gt;堆溢出：不断创建对象同时不被回收，（-Xmx-Xms来设置堆最大值与最小值）&lt;/p&gt;

&lt;p&gt;栈溢出：不断创建本地变量， 通过设置-Xss栈内存容量
方法区溢出：在OutOfMemoryError会跟着PermGen space  （-XX:PermSize -XX:MaxPermSize设置方法区大小）&lt;/p&gt;

&lt;h4 id=&quot;三垃圾收集器与内存分配策略&quot;&gt;三、垃圾收集器与内存分配策略&lt;/h4&gt;
&lt;p&gt;过程：设置安全点等待线程到达进行GC，根据可达性算法分析对象是否收集，通过收集算法处理内存。实现就是垃圾收集，根据新生代和老牛年还有CPU等分了很多种&lt;/p&gt;

&lt;h5 id=&quot;1垃圾收集&quot;&gt;1.垃圾收集&lt;/h5&gt;
&lt;p&gt;程序计数器，虚拟机栈，本地方法栈：随线程而生而灭，栈帧也基本是类结构就确定，无须过多考虑，主要考虑堆和方法区&lt;/p&gt;

&lt;h5 id=&quot;2判断对象是否存活&quot;&gt;2.判断对象是否存活&lt;/h5&gt;
&lt;p&gt;2.1.引用计数算法&lt;/p&gt;

&lt;p&gt;通过对象中添加计数器，有引用加1，失效减1，为0不被使用。无法解决2个对象互相应用（oa.i=ob,ob.i=oa）虚拟机不是使用这种&lt;/p&gt;

&lt;p&gt;2.2.可达性分析算法&lt;/p&gt;

&lt;p&gt;以GC Roots作为起点开始向下搜索引用链，当一个对象到GC Roots没有引用链就被当做不可用。（栈中的引用的对象，方法区引用的对象都可以作为GC Roots）&lt;/p&gt;

&lt;h5 id=&quot;3引用&quot;&gt;3.引用&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;强引用：Object a = new Object();只要强引用，永远不会被回收掉
软引用：有用但非必须的对象，在系统快发生内存溢出之前，会对这些对象进行二次回收，如果还不够才会抛出异常
弱引用：有用但非必须的对象，比软引用更弱一些，只能生存到下次垃圾收集发生之前
虚引用：最弱的引用，在被对象回收时收到一个系统通知
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;4对象的回收&quot;&gt;4.对象的回收&lt;/h5&gt;
&lt;p&gt;不可达对象，在第一次标记之后会执行finalize方法，如果在改方法中又重新建立了关联，就不会被回收，如果第二次被回收，即使在finalize中关联也没用，也会被回收。只能拯救一次&lt;/p&gt;

&lt;h5 id=&quot;5垃圾收集算法&quot;&gt;5.垃圾收集算法&lt;/h5&gt;
&lt;p&gt;5.1.标记清除算法：标记和清除两个阶段，先标记需要回收的对象，在标记完统一回收所有被标记对象（效率不高，产生内存碎片）&lt;/p&gt;

&lt;p&gt;5.2.复制算法：把内存一份为二，每次只用其中一块，回收时，把存活的对象全部复制到宁外一块上，在一次性清理使用过的内存，（不用考虑内存碎片，浪费了一半内存）。新生代就是使用这种(回收率高)&lt;/p&gt;

&lt;p&gt;5.3.标记整理算法：标记过程与标记清除算法一致，在整理时，让存活对象全部向一端移动，然后直接清除掉端边界以外内存。（适用老年代）&lt;/p&gt;

&lt;p&gt;5.4.分代收集算法：大部分虚拟机用这种，把堆划分老年代，新生代，根据年代特点，选择适合的算法。&lt;/p&gt;

&lt;h5 id=&quot;6hotspot算法实现何时进行gc&quot;&gt;6.HotSpot算法实现（何时进行GC）&lt;/h5&gt;
&lt;p&gt;6.1.枚举根节点：GC的时候为了保证一致性要系统停顿，使用OopMap来保存引用位置，GC扫描就可以直接知道信息。&lt;/p&gt;

&lt;p&gt;6.2.安全点：为了不让每条指令都生成OopMap，需要设置安全点，在特定位置记录。在GC的时候让所有线程都到安全点。&lt;/p&gt;

&lt;p&gt;6.2.1.抢先式中断：把全部线程停止，恢复不再安全点的线程，让它运行到安全点。&lt;/p&gt;

&lt;p&gt;6.2.2.主动式中断：GC时候设置标志位，所有线程自己轮询判断是否到达。（都采用这种）&lt;/p&gt;

&lt;p&gt;6.3.安全区域：有些Sleep状态的线程无法走到安全点，引入安全区域（拉伸的安全点），在安全区域的任意地方GC都是安全的。&lt;/p&gt;

&lt;h5 id=&quot;7垃圾收集器&quot;&gt;7.垃圾收集器&lt;/h5&gt;
&lt;p&gt;7.1.Serial收集器：最基本，早悠久的收集器，使用单线程收集，使用复制算法，收集时停止全部线程（使用于新生代，Client模式下的虚拟机，简单高效）&lt;/p&gt;

&lt;p&gt;7.2.ParNew收集器：Serial收集器的多线程版本。默认开启的线程数等于cpu数量，在不是非常多的CPU下，效率不如Serial收集器&lt;/p&gt;

&lt;p&gt;7.3.Parallel Scavenge收集器：也是多线程新生代收集器。专注于吞吐量，运行代码时间和垃圾收集时间的比例（MaxGCPauseMillis,GCTimeRatio设置）&lt;/p&gt;

&lt;p&gt;7.4.Serial Old收集器：Serial收集器的老年代版本，使用标记整理算法&lt;/p&gt;

&lt;p&gt;7.5.Parallel Old收集器：Parallel
Scavenge收集器的老年代版本，使用多线程和标记整理算法&lt;/p&gt;

&lt;p&gt;7.6.CMS收集器：基于标记-清除算法，初始标记（stop world），并发标记，重新标记，并发清除&lt;/p&gt;

&lt;p&gt;7.7.G1收集器：最新的收集器。&lt;/p&gt;

&lt;h5 id=&quot;8内存分配策略&quot;&gt;8.内存分配策略&lt;/h5&gt;
&lt;p&gt;8.1.对象优先在Eden分配&lt;/p&gt;

&lt;p&gt;8.2.大对象直接进入老年代：-XX:PretenureSizeThreshold 来设置对象的限制大小，超过就是大对象&lt;/p&gt;

&lt;p&gt;8.3.长期存活的对象进入老年代：根据年龄来判断对象（每次minor gc 年龄加1）-XX:MaxTenuringThreshold设置年龄限制&lt;/p&gt;

&lt;p&gt;8.4.动态对象年龄判定：在Survivor空间相同年龄的对象大小之和大于空间一半，年龄大于等于的对象都进入老年代&lt;/p&gt;

&lt;p&gt;8.5.分配空间担保：HandlePromotionFailure来设置是否启用。判定老年代的连续空间是否大于新生代所以对象总空间。如果开启，在没有大于，会判定每次晋升老年代的平均大小是否超过，超过了就进行full gc，没开启也进行 full gc。&lt;/p&gt;

&lt;h4 id=&quot;三性能监控与故障处理工具&quot;&gt;三、性能监控与故障处理工具&lt;/h4&gt;
&lt;h5 id=&quot;1jdk的命令行工具bin里面的程序都是对toolsjar的包装实际实现都在jar内部&quot;&gt;1.JDK的命令行工具(bin里面的程序都是对tools.jar的包装，实际实现都在jar内部)&lt;/h5&gt;
&lt;p&gt;1.1.jps：虚拟机进程状况工具—-可以列出正在运行的虚拟机进程，并显示虚拟机执行的主类名称以及唯一ID（LVMID）
如：jps -l：输出全部信息，-v：启动指定的参数列表&lt;/p&gt;

&lt;p&gt;1.2.jstat：虚拟机统计信息监视工具—-显示类装载、内存等运行数据，非gui图形界面，定位虚拟机性能首选工具&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jstat  option vmid interval count （option：-class，-gc，-gcutil等）
如：jstat -gc 2764  250 10 监控进程2764的gc情况，250毫秒查询一次查询20次
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;1.3.jinfo：Java配置信息工具—-实时查看和调整虚拟机各项参数
如：jinfo -flag CMSInitiatingOccupancyFraction 14032&lt;/p&gt;

&lt;p&gt;1.4.jmap：Java内存映像工具—-生成堆转储快照文件（headdump）
如：jmap -dump:format=b,file=xx 14032&lt;/p&gt;

&lt;p&gt;1.5.jhat:虚拟机堆转储快照分析工具—-与jmap配合使用分析jmap生成的快照
如：jhat xx，可以打开浏览器查看&lt;/p&gt;

&lt;p&gt;1.6.jstack:Java堆栈跟踪工具—-生成当前时刻的线程快照，定位长时间停顿原因等
如：jstack -l 14032&lt;/p&gt;

&lt;p&gt;1.7.HSIDS:JIT生成代码反汇编&lt;/p&gt;

&lt;h5 id=&quot;2jdk的可视化工具&quot;&gt;2.JDK的可视化工具&lt;/h5&gt;
&lt;p&gt;2.1.JConsole：Java监视与管理控制台—-基于JMX的可视化工具&lt;/p&gt;

&lt;p&gt;2.2.VisualVM：多合-故障处理工具–最强大的监视处理工具&lt;/p&gt;

&lt;p&gt;可添加插件：工具-&amp;gt;插件&lt;/p&gt;

&lt;h4 id=&quot;四调优案例分析与实战&quot;&gt;四、调优案例分析与实战&lt;/h4&gt;
&lt;p&gt;1.在16GB内存上，分配了12GB的堆内存导致FullGc时间很长——使用集群来部署，一个应用2GB。或者能控制FullGc的频率很低&lt;/p&gt;

&lt;p&gt;2.集群间同步导致内存溢出，网络情况不好的情况下，所有请求都在等待，导致内存溢出——不应当过于频繁的写操作&lt;/p&gt;

&lt;p&gt;3.堆外内存导致溢出，使用nio，jni代码，这些使用的内存不在堆中，分配不大的情况，也不会自动gc，会导致溢出&lt;/p&gt;

&lt;p&gt;4.外部命令导致系统缓慢，使用Runtime.getRuntime().exec()执行shell脚本，消耗很大，占用cpu。减少这种操作&lt;/p&gt;

&lt;p&gt;5.服务器JVM进程崩溃，异步调用外部接口，外部系统缓慢拖累，导致等待线程增多，使用生产消费队列模式&lt;/p&gt;

&lt;h4 id=&quot;五类文件结构&quot;&gt;五、类文件结构&lt;/h4&gt;
&lt;h5 id=&quot;1无关性的基石&quot;&gt;1.无关性的基石&lt;/h5&gt;
&lt;p&gt;jvm仅仅要求是class文件，可以支持其他语言，能编译成class文件即可，例如JRuby，Groovy等&lt;/p&gt;

&lt;h5 id=&quot;2class类文件结构&quot;&gt;2.class类文件结构&lt;/h5&gt;
&lt;p&gt;2.1.Class文件以8位字节的二进制流，使用无符号数和表来存储数据&lt;/p&gt;

&lt;p&gt;2.2.Class头4字节为魔数，确定文件是否是class文件。值为（0xCAFEBABE）后面4字节，5-6字节是次版本号，7-8是主版本号，jdk从45开始计算&lt;/p&gt;

&lt;p&gt;2.3.常量池：跟在主次版本之后，使用2字节标识常量的数量，每一种常量都使用不同的表结构来存储，如：CONSTANT_Class_info 7 类或接口的符号引用，0x07：代表是class类型，class_info的结构是u1：tag，u2：name_index，每个常量表结构第一位都是u1标志位表示常量类型。&lt;/p&gt;

&lt;p&gt;一般class方法、字段都引用CONSTANT_Utf8_info来描述，它的length是u2即：65535，所以定义不能超过64KB英文字符。&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;2.4.访问标志：在常量池之后，u2来识别，有16个标志位可以使用（65535个），目前自定义了8个，如：0x0001：是否public类型，0x0010：是否final，计算满足的做一个&lt;/td&gt;
      &lt;td&gt;运算：0x0001&lt;/td&gt;
      &lt;td&gt;0x0010=0x0011，即是标志的值&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;2.5.类索引，父类索引，接口索引：占位：类索引：u2，父类索引：u2（java只能单继承），接口索引：u2（表示计数器，接口的索引的个数），后面跟着计数个u2接口索引。索引就是去常量池中查找第n个常量&lt;/p&gt;

&lt;p&gt;2.6.字段表集合：标志位：u2（同类的访问标志，定义变量的public，final等），&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name_index：u2（字段的简单名称的常量池索引）比如：inc(), m简单名称：inc,m ，
descriptor_index：u2（字段和方法的描述符的常量池索引），描述符:描述字段的数据类型(使用标识字符：byte用B，对象用L)，方法的参数列表和返回值，如：String[][] 描述符：[[Ljava/lang/String，void inc(int num)描述符:(I)V
attributes_count：u2（标识额外信息的个数）
attributes:额外信息
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.7.方法表集合：同上已做了说明，方法内部的代码会放在attributes一个code的属性里面。如果没有重写父类方法，方法集合表是不会出现的&lt;/p&gt;

&lt;p&gt;2.8.属性表集合：属性名称：u2（常量池的索引），属性的长度：u4，属性的内容&lt;/p&gt;

&lt;p&gt;常见属性：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Code：程序方法体中的代码经过编译变成字节码指令存储在Code属性。其中有max_stack（u2）:操作数栈深度的最大值
Exceptions：列举出方法中可能抛出的受查异常（throws 后面的异常）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;3字节码指令简介&quot;&gt;3.字节码指令简介&lt;/h5&gt;
&lt;p&gt;3.1.jvm的指令有一个字节长度的、代表某种特定操作含义的数字（操作码）以及随后0-n个代表此操作所需参数（操作数）构成
一个字节限制了操作码总数不能超过256个&lt;/p&gt;

&lt;p&gt;3.2.加载和存储指令：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iload、fload等：将一个数值从局部变量表加载到操作数栈
istore、fstore等：将一个数值从操作数栈存储到局部变量表
bipush、ldc、lconst等：常量加载到操作数栈
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.3.运算指令：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iadd（加法），isub（减法），imul（乘法），idiv（除法），irem（求余），ineg（取反），ishl（位移），ior（按位或），iand（按位与），ixor（按位异或），iinc（局部变量自增），dcmpg（比较）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.4.类型转换指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;窄化类型转换时，必须显式的使用转换指令完成。i2b、i2c、i2s、l2i等
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.5.对象创建和访问指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;new：创建实例，newarray、anewarray创建数组，
getfield、putfield，getstatic，putstatic：访问类字段和实例字段，
baload、caload等：数组元素加载到操作数栈，bastore、castore等：将一个操作数存储到数组元素
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.6.操作数栈管理指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pop、pop2：将操作数栈的栈顶一个或两个元素出栈
swap：将栈最顶端两个数值交换
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.7.控制转移指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ifeq、iflt、ifnull、if_icrmplt等：条件分支
tableswitch、lookupswitch：复合条件分支
goto、jsr、ret：无条件分支
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.8.方法调用和返回指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;invokevirtual：调用对象的实例方法，根据对象的实际类型进行分派
invokeinterface：调用接口方法，在运行时搜索一个实现方法进行调用
invokestatic：调用类方法（static方法）
ireturn、lreturn等：方法返回
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.9.异常处理指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;athrow：显示抛出异常的操作（throw语句）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.10.同步指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;monitorrenter、monitorrexit：支持synchronized关键字的语义
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;六虚拟机类加载机制&quot;&gt;六、虚拟机类加载机制&lt;/h4&gt;

&lt;p&gt;未完待续&lt;/p&gt;

</description>
        <pubDate>Thu, 18 Jan 2018 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/java/2018/01/18/%E6%B7%B1%E5%85%A5JVM%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/java/2018/01/18/%E6%B7%B1%E5%85%A5JVM%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.html</guid>
        
        <category>Java</category>
        
        <category>虚拟机</category>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>2017年终感悟</title>
        <description>&lt;p&gt;2017年过完了，今天准备写一下总结，发现自己这两年的博客写的好少，可能实在变懒了，不应该啊，以后需要改变。跟以前一样继续流水账记录年终总结。&lt;/p&gt;

&lt;p&gt;2月，过完年回公司上班，来公司大半年，由于公司收益不好，整个部门的年终奖都很低，自己所在的小组也没从事公司的项目，自己也有点想离开，后来想想才来半年又换公司感觉太飘了。想沉淀一下，学习一些新知识。也打消了换工作的想法。继续去年项目的维护。&lt;/p&gt;

&lt;p&gt;3-4月，这2个月走了不少同事，我也无所事事了很久，去年的项目也感觉是烂尾了。我就想自己写一个开源的项目，关于工作流，流程表单这些常用模块的系统。同时也买了一本深入jvm虚拟机，希望对自己有帮助。这是给今年定的期望，完成流程系统plumdo-work，同时看完jvm的书。&lt;/p&gt;

&lt;p&gt;5-6月，去年项目取消后，我被安排到了部门的新项目酒吧系统，公司为了利润目标，想用ktv的系统做一下修改打入酒吧行业。顺便介绍一下公司的线下系统。使用Delphi开发客户端直连数据库调用存储过程实现，都是给用户直线部署系统，相对并发量很低对系统性能要求也比较低。安排我的任务就是修改他们原来的存储过程，看完sql脚本，说实话心里很烦躁，一个功能可以嵌套10层的存储过程，改的都是云里雾里的，不明白做了10年的系统为什么还要用这种架构，之前听说每3年一个新版本，其实都只是修改了客户端的样式。由于在之前打定了再继续工作一年看看，我就开始写村过程，不过安排的任务也不多，空闲时间不少，就使用angularJS模范bootstrap的设计器开发了表单设计器plumdo-form。&lt;/p&gt;

&lt;p&gt;7月，空闲的一个月，每天上班写一下流程设计器，同时也看了一下SpringBoot，SpringCloud的基础入门。下班由于跟我一起打篮球的学生放暑假了，每天晚上都一起打篮球，这期间瘦了不少。自己也蛮开心的，无忧无虑的一个月。&lt;/p&gt;

&lt;p&gt;8月，封闭开发开始，由于公司之前ktv的系统线上功能是交给宁外一个子公司开发，现在酒吧系统，销售领导想我们自己做，直接拿了子公司的代码,想抄过来结果安排的任务那些开发人员都没做，领导得知后，决定去酒店封闭开发。我改的存储过程相对内容不多，就安排我也加入开发线上与线下的数据同步交互。封闭的时间，每天早上9点做到晚上11点30，基本做完就是睡觉，一周就休息半天。发现真心有点累，不过公司安排的伙食都是酒店一桌菜还算不错。&lt;/p&gt;

&lt;p&gt;9月，原来打算封闭3周的，结果好像和预期出入很大。整个线上后台业务开发就来一个新员工，加上需求不明确，部门经理对技术架构也没接触过，结果出现做完发现和需求不符重做。一直延期。我开发完数据同步之后。我就顺便也帮忙开发在线超市点单，绑卡台等功能，以及负责线上环境的搭建，公司打算线上系统也是拿来做方案卖，所以需要部署方便，由于涉及的服务比较多：rabbitmq，redis，nginx，php，java，mysql等，就打算用docker，我就在阿里云服务器上用docker搭建整个环境。由于也没怎么接触运维的事情，中间也踩了一些docker和运维的坑，不过好歹自己也学了一个新知识点。最后本来打算封闭开发在线超市，电子会员卡，大屏等线上功能，最后由于国庆到了，也只开发了在线超市，封闭就这样结束了。&lt;/p&gt;

&lt;p&gt;继续说一下封闭的事情，在封闭的期间跟项目经理吵过几次，其他同事做的慢就推过来我做，系统架构也没有设计，也没有任务安排计划。我比较在意整个系统的设计以及代码规范，加上直性子有事不能憋着，就经常跟项目经理直接吵架。加上天天的高强度加班，其实我们每个同事到后面基本到晚上加班的时候，都是在打诨，所以我感觉有时候高强度不一定效率会更高。合理的前期设计和安排其实是系统开发的重点。&lt;/p&gt;

&lt;p&gt;10月，在线超市开发完，我负责连锁会员的开发，参照了用来ktv的模式，感觉不太适合现在的架构了，原来ktv一个总店对应几个分店做数据同步，都是部署在客户的服务器，但是现在用云系统，多个总店对数据库压力很大，我就修改了原来的模式，使用时间戳做标识来同步数据。整体逻辑差不多。期间线下的系统找到了酒吧上线了，结果原来的ktv模式直接被酒吧的数据了刷崩了，各种死锁，数据错误。专门负责存储过程的同事去现场晚上加班改，后来改到没办法改的地步，被限制在存储过程，扩展性实在太差。&lt;/p&gt;

&lt;p&gt;11月，其他同事之前开发的电子会员卡，我负责与线下的对接，回想过来，每个模块我都做了，电子会员卡的功能负责不多，我们线上的功能也找了一家试点，不过由于只有在线超市，客户不满意退货了。&lt;/p&gt;

&lt;p&gt;12月，项目经理让我负责整个线上系统，说线下的存储过程问题太大，他也没办法管理2边。同时第一次面试，招聘了一个java新员工。我就负责了新模块在线预订，新来的员工，感觉水平不是很好，不过公司由于给不起高工资只能要他了，其实我们公司对技术要求不需要太高，只要肯上心就好，结果新同事观察了一个多月，感觉每天都不知道在干嘛，问业务都不懂，也是醉了。整个月就负责安排在线预订功能开发，其实总体的功能复杂度都不高，加上有子公司的参考其实不算太难。&lt;/p&gt;

&lt;p&gt;在负责项目之后，发现确实对自己有些提高，大家开发有问题就是问我，我也没开发过，只能自己多看看，大体把控一些需求。最近几个月由于开发了线上系统，整个时间也比较紧，加上自己下班也不想做什么事情，导致年初定的目标，就完成了表单和流程设计器，jvm的书也只看了一半。感觉自己对目标的坚持还是不太久。&lt;/p&gt;

&lt;p&gt;最后，说说这一年的感情生活，继续的相亲，去年就2次，今年也不多，过年相亲了一次，一个准备做it的妹子，后来没什么感觉就没联系了，后来6月的时候，连续介绍了3个，财务，老师，护士，都只聊了3周不到，见过一个。自己也试着改变，公司的活动也试着去参与。希望自己变的外向和妹子有话题可以聊。&lt;/p&gt;

&lt;p&gt;一眨眼也快30了，俗话三十而立，不过自己现在一事无成，今年参加了好多场朋友的婚礼，大部分朋友都结婚了，自己也不知道什么时候可以定下来。本来打算明年辞职，去外面走走，现在好像这种想法也不强烈了。未来的事情确实不需要太多的去想，顺其自然吧。&lt;/p&gt;

&lt;p&gt;前2月，父母突然想买房，来福州看房子，看完，发现福州的房价确实高，05年的二手房都要2w7一平米，对比自己的工资确实可怕，父母也是怕房子再涨买不起了，以后没房，找不到老婆，不过我其实对房子的想法不强烈，感觉中国的环境，一套房基本就捆绑了一辈子的经济，不敢在肆无忌惮的玩耍，天天想着房贷，那种生活太累了。年轻一代不应该是这样的生活状态的，但是实际情况，大家都在为了房子拼命，失去了自己本该有的生活态度。不知道是人的悲哀，还是国家的悲哀。&lt;/p&gt;

&lt;p&gt;自己也玩了股票2年多，结果还是处于亏损，虽然亏损不多，但是自己投资还是不太成功，不过对于以后不去投资也不太现实，父母都是在老家做标会，风险很大，经常有人跑路。自己以后的存款我还是希望可以去做一些投资，我对现在的p2p也有点怕，虽然中国股市被人天天骂，不过好歹也是一个门路吧。为了股票我自己也搞了一个采集股票的交易信息的项目：plumdo-stock，希望对后面的投资股票有帮助吧。股市给我的感觉就是磨练人性，想要玩好确实不容易。&lt;/p&gt;

&lt;p&gt;对明年的期望，今年的一个惊喜是我的plumdo-work项目居然有9人fork，挺意外的，给了我继续做下去的动力，争取明年能做出第一版完整版。看完jvm书，顺便看看投资类的书籍。明年对于是否换工作其实也挺纠结。现在的公司，公司安排的工作对自己提升蛮大，不过公司的业绩很差，基本加工资没戏，2年了没加过工资，看着福州的房价，我也变了，变的开始追逐钱。&lt;/p&gt;

&lt;p&gt;最近喜欢上了郑智化，可能是因为他的很多歌很符合我现在的处境吧。以中产阶级的结尾做Ending——头壳坏掉才能够活得很好。&lt;/p&gt;

</description>
        <pubDate>Fri, 05 Jan 2018 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2018/01/05/2017%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2018/01/05/2017%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</guid>
        
        <category>工作</category>
        
        
        <category>工作</category>
        
      </item>
    
      <item>
        <title>Docker使用总结</title>
        <description>&lt;p&gt;最近从事公司的新项目，开发一整套云端系统。由于公司没有专门的运维人员，加上之前的项目都是基于线下的delphi系统。我就顺便负责前期的环境搭建工作，由于公司的整个云系统不仅仅做云服务提供给其他商家使用，还可能做为解决方案卖给大客户，需要部署方便，加上云端的依赖环境主要是mysql，rabbitmq，redis，nginx，php，java。暂定方案使用docker来做环境搭建。&lt;/p&gt;

&lt;p&gt;docker的入门教程蛮多的，我就做简单介绍一下。个人理解的docker，作为一种容器的技术，通过从统一的仓库运输镜像到容器去运行，通过linux的底层技术LSC实现隔离，提供统一的API接口方便部署。&lt;/p&gt;

&lt;h2 id=&quot;安装以centos7为例&quot;&gt;安装（以centos7为例）&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;安装
yum install -y docker
启动
service docker start
验证安装
docker version

由于网络问题，可能拉取镜像的时候很慢，可以通过设置代理等操作加快，或者自建镜像仓库等
贴一下我之前有用到仓库中心：
Docker仓库
https://hub.docker.com/explore/
国内：需要登录
https://cr.console.aliyun.com/#/imageList
https://c.163.com/hub#/m/home/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;主要指令&quot;&gt;主要指令&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1.docker pull [options] name[:TAG] 拉取镜像:不加标签默认获取latest

2.docker run [options] image[:TAG][COMMAND]
运行镜像：指令的选项较多，介绍自己平常使用较多的
-d:后台运行
-p:指定端口端口映射
-P:启动全部端口映射，需要通过docker ps查看端口映射情况
-v:文件映射
-e:变量赋值
-m:限制容器的内存上限
--name:容器命名
--restart=always:容器重启

3.docker ps:查看运行的容器
-a:查看全部，包括已经停止

4.docker exec -it containid bash:进入容器内部

5.docker stats containid:查看容器的运行情况

6.docker build -t image:tag -f buildfile .:构建镜像
构建镜像：有时候我们需要为存在的镜像加入新的插件来重新构建特有的镜像，通过编写Dockerfile来构建

贴一个自己构建php的镜像（基于基础php加入redis，mq等插件）
docker build -t php:5.6-fpm-ext -f ./docker/php/phpDockerfile .

phpDockerfile:

FROM hub.c.163.com/library/php:5.6-fpm
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y  libfreetype6-dev  libjpeg62-turbo-dev  libmcrypt-dev  libpng12-dev 
RUN docker-php-ext-install -j$(nproc) iconv mcrypt 
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ 
RUN docker-php-ext-install -j$(nproc) gd
RUN docker-php-ext-install mysqli
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install zip
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/2.2.7.tar.gz \
    &amp;amp;&amp;amp; tar xfz /tmp/redis.tar.gz \
    &amp;amp;&amp;amp; rm -r /tmp/redis.tar.gz \
    &amp;amp;&amp;amp; mkdir -p /usr/src/php/ext \
    &amp;amp;&amp;amp; mv phpredis-2.2.7 /usr/src/php/ext/redis \
    &amp;amp;&amp;amp; docker-php-ext-install redis
COPY ./docker/php/rabbitmq-c-0.4.1.tar.gz  /tmp/rabbitmq-c-0.4.1.tar.gz
RUN tar zxvf /tmp/rabbitmq-c-0.4.1.tar.gz \
    &amp;amp;&amp;amp; curl -L -o /tmp/amqp-1.2.0.tgz http://pecl.php.net/get/amqp-1.2.0.tgz \
    &amp;amp;&amp;amp; tar zxvf /tmp/amqp-1.2.0.tgz \
    &amp;amp;&amp;amp; rm -r /tmp/rabbitmq-c-0.4.1.tar.gz \
    &amp;amp;&amp;amp; rm -r /tmp/amqp-1.2.0.tgz \
    &amp;amp;&amp;amp; mkdir -p /usr/src/php/ext \
    &amp;amp;&amp;amp; mv rabbitmq-c-0.4.1 /usr/src/php/ext/rabbitmq \
    &amp;amp;&amp;amp; mv amqp-1.2.0 /usr/src/php/ext/amqp \
    &amp;amp;&amp;amp; cd /usr/src/php/ext/rabbitmq \
    &amp;amp;&amp;amp; ./configure --prefix=/usr/local/rabbitmq-c-0.4.1 \
    &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make install \
    &amp;amp;&amp;amp; cd /usr/src/php/ext/amqp \
    &amp;amp;&amp;amp; docker-php-ext-configure amqp --with-php-config=/usr/local/bin/php-config --with-amqp --with-librabbitmq-dir=/usr/local/rabbitmq-c-0.4.1/ \
    &amp;amp;&amp;amp; docker-php-ext-install amqp 


&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题事项&quot;&gt;问题事项&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;防火墙问题：
在一个容器内容调用宁外一个容器的url出现curl：no route to host问题。
经排查：我的centos7防火墙使用了firewalld，firewalld的底层是使用iptables进行数据过滤，建立在iptables之上，这可能会与 Docker 产生冲突。
解决：在firewalld中加入容器需要端口
firewall-cmd --zone=public --add-port=80/tcp --permanent:加入80端口
firewall-cmd --reload:重启防火墙

权限问题：
有时候会出现无法写文件等问题
解决：（最粗暴的方案）
给文件加入chmod 777 -R *
docker加入docker run --privileged=true

JAVA内存问题：
使用springboot的时候出现内存吃紧，由于没设置jvm的内存大小默认直接使用机器内存的1/4作为上限，导致内存吃紧。
通过加入docker run -m 只是限制容器的内存上限，到达上限之后容器会被停掉，但是不会对jvm的内存有影响。
要解决应该在启动容器的时候加入 java -Xmx256m -Xms64m -Xmn32m。
当然也可以通过其他方案设置jvm，看个人喜好。

代码和镜像打包问题：
docker使用的时候，代码是否需要和镜像一起构建变成新的镜像，这种方案可能很美好，构建完，哪里有需要拉取，运行就好。
但是在实际运用过程，业务的代码是要经常变更的，所以我们选取的方案是通过-v映射代码，镜像以运行环境为准，不做代码打入，这样变更代码，只需替换文件，无须重构建镜像。

数据库问题：
在我们的开发和测试docker也做了mysql数据库，不过看了网络的一些介绍，说不建议把数据库使用docker，可能会出现问题。由于我们生产环境使用了阿里云的数据库，所以暂时没有发现相关的坑。

应用问题：
docker的容器创建，关闭等通过什么方式？docker compose，还是shell？
考虑到要给其他客户安装加上网络问题，我直接使用shell脚本编写，没有使用compose。最后贴一些我的非专业运维的shell脚本。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;相关扩展&quot;&gt;相关扩展&lt;/h2&gt;
&lt;p&gt;1.docker的网络介绍，docker有3种网络类型：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Bridge（默认）  Host  None
Bridge：做一个网桥端口映射，容器有独立ip，端口，
Host：容器内网络和主机网络一样
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.docker compose
管理多个容器，通过编写docker-compose.yaml来定义需要容器&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;安装：curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
授权：chmod +x /usr/local/bin/docker-compose
查看：docker-compose --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;贴一个docker-compose.yaml&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;version: '2'
services:
  java:
    build: .
    ports:
      - &quot;8080:8080&quot;
    volumes:
      - &quot;./tmp:/tmp&quot;
      - &quot;./logs:/opt/logs&quot;
    depends_on:
      - mysql

    entrypoint: [ &quot;sh&quot;, &quot;-c&quot;, &quot;./wait-for-it.sh -h mysql -p 3306 -s -- java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.datasource.url=jdbc:mysql://mysql:3306/mq -jar app.jar&quot; ]

  mysql:
    image: daocloud.io/mysql:5.6
    environment:
      MYSQL_DATABASE: mq
      MYSQL_USER: mq
      MYSQL_PASSWORD: 123456
      MYSQL_ROOT_PASSWORD: 123456
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.docker可视化UI控制台
通过可视化的页面来控制容器启停，运行状态等。比较流行的有dockerui，Shipyard等，看个人需要。&lt;/p&gt;

&lt;h2 id=&quot;结尾&quot;&gt;结尾&lt;/h2&gt;

&lt;p&gt;最后启动容器的shell脚本：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;start docker&quot;
service docker start

function start_image {
  if [ $(docker ps  -a | grep -c $1) -ge 1 ]
  then
    echo &quot;container has exits $1 start&quot;
    docker start $1
  else
    echo &quot;$1 run image create container&quot;
    docker run -d  -v /etc/localtime:/etc/localtime:ro --privileged=true --name $1 $2
  fi
}


if [ $(docker images hub.c.163.com/library/mysql:5.6 | grep -c hub.c.163.com/library/mysql) -ge 1 ]
then 
  start_image cloud-mysql-dev \
    &quot;-p 8000:3306 
    -v ${PWD}/data/mysql:/var/lib/mysql 
    -v ${PWD}/docker/mysql:/etc/mysql/conf.d  
    -e MYSQL_ROOT_PASSWORD=123cloud 
    hub.c.163.com/library/mysql:5.6&quot; 
fi


if [ $(docker images hub.c.163.com/library/redis:3 | grep -c hub.c.163.com/library/redis) -ge 1 ]
then 
  start_image cloud-redis-dev \
    &quot;-p 8001:6379 
    -v ${PWD}/data/redis:/data 
    -v ${PWD}/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf 
    hub.c.163.com/library/redis:3
    redis-server /usr/local/etc/redis/redis.conf  
    --appendonly yes &quot;
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Fri, 27 Oct 2017 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E8%BF%90%E7%BB%B4/2017/10/27/Docker%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E8%BF%90%E7%BB%B4/2017/10/27/Docker%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93.html</guid>
        
        <category>运维</category>
        
        <category>组件</category>
        
        
        <category>运维</category>
        
      </item>
    
      <item>
        <title>Web前端自动化入门总结</title>
        <description>&lt;p&gt;周末失眠，周一无力写代码，只能听着一首：逆流成河，顺便把最近刚入门的前端自动化写一下入门总结。&lt;/p&gt;

&lt;p&gt;因为从事的开发重点是java后端，后端这几年随着maven，gradle等构建工具的使用也把以前jar管理解放了，记得刚毕业那会公司的方案是公司自己维护一个libs文件夹，把需要的jar放进去，然后ant写脚本打包。这种方式的缺点要自己记住jar的版本等等，然后更新比较繁琐，还有把jar提交到svn等版本管理工具，大小很大，共享麻烦。&lt;/p&gt;

&lt;p&gt;说到前端自动化，现在应该是比较普及了，到github上面搜索一下web组件基本都是用到几个常用的构建组件。不过身处二线城市，一直从事企业系统开发的我，平常的开发，还是按老套路，直接建文件夹像libs，asset等，然后把需要的第三方库，像Jquery，easyui，angular之类的下载复制进去，然后开始写代码。这种方式缺点：同样没有统一管理web库，文件压缩合并等也没有考虑，每次写代码调试一直F5刷新，还时不时缓存没刷掉，上传git文件大小也是非常大。&lt;/p&gt;

&lt;p&gt;最近自己空闲想做一个表单设计器，定了框架angularJs，bootstrap3。然后想用上前端自动化构建工具。开始分批介绍这几个工具：nodejs，yeoman，bower，grunt，gulp。&lt;/p&gt;

&lt;h2 id=&quot;1nodejs&quot;&gt;1.NodeJS&lt;/h2&gt;
&lt;p&gt;NodeJS这个神器就不做介绍，入门nodejs，可以发现一个新的世界，这是最好的诠释。这几个自动化构建工具都是基于nodejs使用。&lt;/p&gt;

&lt;h5 id=&quot;一安装&quot;&gt;一、安装&lt;/h5&gt;
&lt;p&gt;百度一下nodejs，根据系统版本选择下载安装。新版的基本都会自带npm（node的包管理工具）&lt;/p&gt;

&lt;p&gt;安装完成:node -v ，npm -v 查看版本&lt;/p&gt;

&lt;h5 id=&quot;二配置&quot;&gt;二、配置&lt;/h5&gt;
&lt;p&gt;配置npm的全局模块的存放路径以及cache的路径（可以不做选择默认地址）&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm config set prefix &quot;C:\Program Files\nodejs\node_global&quot;
npm config set cache &quot;C:\Program Files\nodejs\node_cache&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;配置环境变量&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;系统变量下新建&quot;NODE_PATH&quot;，输入”C:\Program Files\nodejs\node_global\node_modules“
用户变量&quot;PATH&quot;修改为“C:\Program Files\nodejs\node_global\”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于GWF原因可以选择修改镜像，或者安装cnpm，我建议选择翻墙软件，就不会在后续使用过程出现问题，这里贴一个我用的http://www.herogo.cc/Register/Index/2352499，注册送免费流量，暂时先用一下。&lt;/p&gt;

&lt;p&gt;如果选择翻墙这里可以忽略&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;配置淘宝镜像：npm config set registry &quot;https://registry.npm.taobao.org&quot;
查看配置：npm config list

安装cnpm：npm install -g cnpm –registry=https://registry.npm.taobao.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;三packagejson&quot;&gt;三、package.json&lt;/h5&gt;
&lt;p&gt;描述npm包的文件，类型maven的pom.xml,bower的bower.json等&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;属性说明：
dependencies：生产环境需要安装的包
devDependencies：开发环境需要的包

版本号：
^:^0.4.1:发布了0.4.1会更新，1.X.X就不会更新
~：~0.4.1:发布了0.4.1会更新，0.5.0就不会更新 

npm install：默认使用当前路径下的package.json安装包
npm install chajian：直接安装包到当前目录的node_modules
-g:全局安装
--save-dev:安装同时写入package.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2yeoman&quot;&gt;2.Yeoman&lt;/h2&gt;
&lt;p&gt;Yeoman是Google的团队和外部贡献者团队合作开发的，他的目标是通过Grunt，Gulp，Bower的包装为开发者创建一个易用的工作流。&lt;/p&gt;

&lt;p&gt;简单点说，在项目初始化的时候用了yeoman，自动帮你构建好基础的文件，还有所需要的bower，gulp，grunt等这些配置。&lt;/p&gt;

&lt;h5 id=&quot;一安装-1&quot;&gt;一、安装&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;官网：http://yeoman.io
安装：npm install -g yo
查看版本：yo --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;二使用&quot;&gt;二、使用&lt;/h5&gt;
&lt;p&gt;以angularJS为例：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;安装generator-angular，其他项目可以上官网搜索
npm install -g generator-angular
cd 项目地址
yo angular 项目名
根据提示选择需要的库
yeoman在使用的时候会提示一些常用库让开发者选择是否需要使用方便使用
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3bower&quot;&gt;3.Bower&lt;/h2&gt;
&lt;p&gt;前端库管理器，用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。现在有些开发者直接使用npm做前端库管理器，看需要先暂时使用bower，至于他和npm的利弊可以搜索文章比对一下。&lt;/p&gt;

&lt;h5 id=&quot;一安装-2&quot;&gt;一、安装&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;官网：https://bower.io
安装：npm install -g bower
查看版本：bower -v
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;二文件&quot;&gt;二、文件&lt;/h5&gt;
&lt;p&gt;.bowerrc:设置bower下载的文件位置，代理，超时时长等&lt;/p&gt;

&lt;p&gt;bower.json：需要依赖的web包管理文件，类似package.json&lt;/p&gt;

&lt;h5 id=&quot;三使用&quot;&gt;三、使用&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;在官网搜索需要的web库，根据名称下载
bower install jquery  --单独下载web包
没在bower注册：
    可以通过github短语安装（用户名/项目名  bower install wengweh/plumdo-word）
    可以通过github地址安装 bower install https://github.com/wengwh/plumdo-word.git
    可以通过URL直接安装 bower install http://www.baidu.com
bower install jquery  --save   --把依赖添加到bower.json
bower install jquery  --save-dev  --把依赖添加到bower.json中的开发依赖
bower install  --直接根据当前目录的bower.json下载
bower init  --生成bower.json文件
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;4grunt&quot;&gt;4.Grunt&lt;/h2&gt;
&lt;p&gt;通过配置一系列的task，定义task处理的事务（例如文件压缩合并、雪碧图、启动server、版本控制等），然后定义执行顺序，来让grunt执行这些task，从而构建项目的整个前端开发流程。&lt;/p&gt;

&lt;h5 id=&quot;一安装-3&quot;&gt;一、安装&lt;/h5&gt;
&lt;p&gt;npm install -g grunt-cli&lt;/p&gt;

&lt;h5 id=&quot;二指令&quot;&gt;二、指令&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;grunt taskname：运行指定task
grunt taskname：target：运行指定task内部的target 
grunt：运行default的task
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;三task&quot;&gt;三、task&lt;/h5&gt;
&lt;p&gt;grunt的功能就是执行一系列的task来实现，所以它的task是非常多的，如果有需要了解可以搜索一下，这里介绍几个常用的task&lt;/p&gt;

&lt;p&gt;1.copy
顾名思义复制文件、文件夹&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;copy:{
    dist:{
          src:'&amp;lt;%= config.app %&amp;gt;/js/index.js',---原地址
          dest:'&amp;lt;%= config.dist %&amp;gt;/js/index.js',--目标地址
    }，
    dist：{
         files:[  --使用数组
                {
                         expand:true,
                         cwd: '&amp;lt;%= config.app %&amp;gt;/',
                         src:'*.html',
                         dest:'&amp;lt;%= config.dist %&amp;gt;/',
                         ext:'.min.html', --修改后缀名
                         extDot:'first',--指定修改的后缀名开始位置
                         flatten:false --不会复制到指定目录
                         rename:function( dest,src){
                                return dest+'js/'+src;
                         }
    
                }，
                {
                     src:'&amp;lt;%= config.app %&amp;gt;/js/index.js',
                     dest:'&amp;lt;%= config.dist %&amp;gt;/js/index.js',
                }
          ]
    }，
    dist_files_object:{
          files:{ --使用键值对
                 '&amp;lt;%= config.dist %&amp;gt;/index.html':'&amp;lt;%= config.app %&amp;gt;/index.html',
                 '&amp;lt;%= config.dist %&amp;gt;/js/index.js':['&amp;lt;%= config.app %&amp;gt;/js/index.js']
         }
      }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.wiredep&lt;/p&gt;

&lt;p&gt;给对应文件写入需要引入的js，css文件。当使用bower管理web库的时候，肯定不希望还要一个个在页面中去引入对应的js，css文件，这个插件就是解决这个的问题。&lt;/p&gt;

&lt;p&gt;3.autoprefixer&lt;/p&gt;

&lt;p&gt;给css文件生成自动根据浏览器生成对应的css内容&lt;/p&gt;

&lt;p&gt;4.watch&lt;/p&gt;

&lt;p&gt;监听文件，有修改执行相应任务。在开发的过程可以实时做出变化，就无须在手动执行指令&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;5.uglify&lt;/p&gt;

&lt;p&gt;js压缩，可以设置是否生成js的对应map&lt;/p&gt;

&lt;h2 id=&quot;5gulp&quot;&gt;5.Gulp&lt;/h2&gt;
&lt;p&gt;Gulp和Grunt的功能定位是基本一致的，这两者经常被拿来做比较。现在好像gulp大有代替grunt的意思。不过对于我们使用者来说，他们的变更影响不算太大，基本task也是类似，所欲无需顾虑太多。&lt;/p&gt;

&lt;h5 id=&quot;安装&quot;&gt;安装&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;官网：gulpjs.com
安装：npm install -g gulp
查看版本：gulp -v 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;最后结语，最近做的plumdo-form表单设计器使用了yeoman，bower，gulp来构建。基本问题不大，用起来也还算顺手。现在的前端变化非常大，从去年angular，vue，react这3大阵营互相争夺，加上现在构建工具也是一大堆（webpack没介绍），可能有些人提出了是不是徒增问题，为什么不回归原始方案。其实各种技术的出现都有他的背景和使用情况。所以还是遵从根据实际情况出发，不一定用上什么技术就是好的，没有就是low的。只有根据项目的需要和背景选择适合自己的才是最好的。&lt;/p&gt;

</description>
        <pubDate>Mon, 22 May 2017 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E5%89%8D%E7%AB%AF/2017/05/22/Web%E5%89%8D%E7%AB%AF%E8%87%AA%E5%8A%A8%E5%8C%96%E5%85%A5%E9%97%A8%E6%80%BB%E7%BB%93.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E5%89%8D%E7%AB%AF/2017/05/22/Web%E5%89%8D%E7%AB%AF%E8%87%AA%E5%8A%A8%E5%8C%96%E5%85%A5%E9%97%A8%E6%80%BB%E7%BB%93.html</guid>
        
        <category>前端</category>
        
        <category>组件</category>
        
        
        <category>前端</category>
        
      </item>
    
      <item>
        <title>2016年终感悟</title>
        <description>&lt;p&gt;2016年在前天结束，延续每年的年终总结。今年对技术的储备较少，总结就多写点心情感悟。还是和以前一样按月份总结。&lt;/p&gt;

&lt;p&gt;2-5月，继续从事工作流的组件开发，基础版完成的情况下，给一个项目组先使用，根据他们反馈的使用情况，做相对应功能的补充和修改。在这段时间看到旁边离职的人很多，可能大部分原因还是因为公司扣年终奖，福利缩水，工资不高等因素。自己的不安的心其实在这一段时间也有点动摇，后来想说看看公司加工资情况，结果到了4月底加工资的时间，居然全部人延后一个月。这时候心里已经有点疙瘩了。然后就在5月初发了一封离职邮件给我组长。后来离职谈话，居然连部门总经理都找谈话，有点意外，可能领导对工作流组件还是蛮重视，在我们这种传统运营商IT公司，领导说过要做到组件化，产品化，才能提高开发效率。与领导交谈，他的话其实还是挺让人触动的。不过就按我当时和领导说的，既然提出要走，基本也不可能留下。&lt;/p&gt;

&lt;p&gt;6月，交接工作和找工作的一个月。在5月提出离职后，准备交接工作，本着好来好散的思想，基本公司要求一一满足，给交接的同事每天讲解代码，给部门开研讨会，说明怎么使用组件等。自己也边交接边找工作。其中面试了2家公司，在这里给第一家说声不好意思。在面试完第一家后拿了offer后，面试第二家公司，最后选择第二家，给第一家发了邮件说没办法去。其实原因也是和邮件说的一样，第一家要求长期出差，自己后来想想虽然第一家待遇还更好，想说出于工作的持续性，可能第二家会更适合。在确定新工作之后就是交接走离职流程。离职流程走了整整2个月，最后心累离开。&lt;/p&gt;

&lt;p&gt;7-9月，这次离职之后没有去旅游，同学和女朋友分手住我那里，想想自己也没什么心情就算了。入职安排在一个新项目组，就3个人开发。我负责后台JAVA开发，前端使用PHP，加入之前研发过的工作流引擎。工作内容倒是不多，因为是新项目基本都是在做框架，类似人员，菜单，权限等基础功能。新公司上班时间倒是蛮轻松还有下午茶，基本零加班，相对于现在的996公司真的很好。&lt;/p&gt;

&lt;p&gt;在这段时间，趁着空闲，学习了一下angularJS，在以前的工作流引擎设计器的源码中用到了这个前端框架，虽然能改的动，但是还是想系统的看一下整个框架。今年前端框架很活跃几大阵营也是胡不妥协，虽然我是一个Java后端程序员，但是还是想接触看看现在一些前端技术，最后使用bootstrap模板和angularJS做结合，做一个demo，当做储备。&lt;/p&gt;

&lt;p&gt;10-12月，加入业务开发，合同，项目，派工等流程业务。基于微信企业号和PC做两个平台的系统，再基于工作流引擎做到可配置，可通知等操作。开发的难度都不是很大，基于微信的api和我熟悉的工作流引擎开发起来也蛮快。在剩余时间，把原来使用的Springmvc+Spring+jdbcTemplate的后台框架做了一下修改，把baseDao做了提炼，一些重复的代码做了调整，让代码尽量好维护。后面一段时间基本就是针对使用的代理商，做一些业务的调整修改。&lt;/p&gt;

&lt;p&gt;1月，刚刚开始，因为元旦的发生的一些事情，就把今年总结先写了。元旦回家，父母吵架，2016年基本每次回家都没和谐过，有时候想想有人追求富贵，追求晋升，而我只希望家庭和睦。年龄大了，单身狗伴随着就是被催婚，从毕业一年后就开始被催，我也逐渐习惯了。只是有时候心里有些不知道怎么去经营婚姻，可能父母的吵架也有点潜移默化的影响，加上现在的房价等生活成本让我从心里也有点抗拒。虽然相亲了好多次，连以前快要订婚的女生也在这个月要结婚了。感觉面对情感问题最重要还是心态问题。&lt;/p&gt;

&lt;p&gt;这一年，技术的学习基本为零，感情进展为零，不过我也养成了自己做饭的习惯，也不是为了节约饭钱，仅仅为了自己为数不多的兴趣。每周生活固定，一周五天班朝九晚六，周末2天和2个00后打篮球。这是单调而又飞快的一年。&lt;/p&gt;

&lt;p&gt;明年打算，说起来也很讽刺，在年初找新工作的时候还为了持续长久，结果才来不到一年我就打算走了。前2次跳槽可以说觉得工资低，这次要走，也不知道为什么，可能心累吧。按马云说的离职原因无非就2点钱不够，心累。本来打算去其他城市，想想也不知道怎么样，父母也不太喜欢。过完年看看。&lt;/p&gt;

&lt;p&gt;最后，用最近看到的一句话形容我现在的生活状况——喜欢上做饭了，喜欢那种认真备料，精心调配，煎炒焖煮后，做出一坨屎的感觉，就像自己的人生。&lt;/p&gt;

</description>
        <pubDate>Tue, 03 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2017/01/03/2016%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</link>
        <guid isPermaLink="true">https://wengwh.github.io/%E5%B7%A5%E4%BD%9C/2017/01/03/2016%E5%B9%B4%E7%BB%88%E6%84%9F%E6%82%9F.html</guid>
        
        <category>工作</category>
        
        
        <category>工作</category>
        
      </item>
    
  </channel>
</rss>
