├── .nojekyll ├── CNAME ├── favicon.ico ├── images ├── other │ ├── deer.jpg │ ├── desert.jpg │ ├── flower.jpg │ ├── galwba.png │ ├── plant.jpg │ ├── sculpture.jpg │ ├── aliyun_sale.jpg │ ├── apache │ │ └── apache.jpg │ └── tomcat │ │ └── tomcat.jpg ├── config │ ├── alipay.jpg │ ├── avatar.ico │ ├── avatar.jpg │ ├── cxy35.ico │ ├── cxy35.jpg │ ├── qw12op90.jpg │ ├── avatar_150.jpg │ ├── avatar_258.jpg │ ├── cxy35_150.jpg │ ├── cxy35_258.jpg │ ├── wechatpay.jpg │ ├── qw12op90_cxy35.jpg │ └── qw12op90_business.jpg ├── linux │ └── linux.jpg ├── mq │ └── rabbitmq │ │ ├── 1.jpg │ │ ├── 1.png │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── 7.jpg │ │ ├── 8.jpg │ │ ├── 9.jpg │ │ ├── 10.jpg │ │ ├── 100.jpg │ │ ├── 101.jpg │ │ ├── 11.jpg │ │ ├── 12.jpg │ │ ├── 13.jpg │ │ ├── 14.jpg │ │ ├── 15.jpg │ │ ├── 16.jpg │ │ ├── 17.jpg │ │ ├── 18.jpg │ │ ├── 19.jpg │ │ ├── 20.jpg │ │ ├── 21.jpg │ │ ├── 22.jpg │ │ ├── 23.jpg │ │ ├── 24.jpg │ │ ├── 25.jpg │ │ ├── 26.jpg │ │ ├── 27.jpg │ │ ├── 28.jpg │ │ ├── 29.jpg │ │ ├── 30.jpg │ │ ├── 31.jpg │ │ ├── 32.jpg │ │ ├── 33.jpg │ │ ├── 34.jpg │ │ ├── 35.jpg │ │ ├── 36.jpg │ │ ├── 37.jpg │ │ ├── 38.jpg │ │ ├── 40.jpg │ │ ├── 41.jpg │ │ ├── 42.jpg │ │ ├── 43.jpg │ │ ├── 44.jpg │ │ ├── 45.jpg │ │ ├── 46.jpg │ │ ├── 47.jpg │ │ ├── 49.jpg │ │ ├── 50.jpg │ │ ├── 52.jpg │ │ ├── 53.jpg │ │ ├── 54.jpg │ │ ├── 55.jpg │ │ ├── 56.jpg │ │ ├── 58.jpg │ │ ├── 59.jpg │ │ ├── 60.jpg │ │ ├── 61.jpg │ │ ├── 62.jpg │ │ ├── 63.jpg │ │ ├── 64.jpg │ │ ├── 65.jpg │ │ ├── 67.jpg │ │ ├── 68.jpg │ │ ├── 70.jpg │ │ ├── 71.jpg │ │ ├── 72.jpg │ │ ├── 73.jpg │ │ ├── 74.jpg │ │ ├── 75.jpg │ │ ├── 76.jpg │ │ ├── 77.jpg │ │ ├── 78.jpg │ │ ├── 79.jpg │ │ ├── 80.jpg │ │ ├── 81.jpg │ │ ├── 82.jpg │ │ ├── 83.jpg │ │ ├── 84.jpg │ │ ├── 85.jpg │ │ ├── 86.jpg │ │ ├── 87.jpg │ │ ├── 88.jpg │ │ ├── 89.jpg │ │ ├── 90.jpg │ │ ├── 91.jpg │ │ ├── 92.jpg │ │ ├── 93.jpg │ │ ├── 94.jpg │ │ ├── 95.jpg │ │ ├── 96.jpg │ │ ├── 97.jpg │ │ ├── 98.jpg │ │ ├── 99.jpg │ │ ├── rpc.png │ │ ├── topics.png │ │ ├── routing.png │ │ ├── hello-world.png │ │ ├── work-queues.png │ │ └── publish-subscribe.png ├── nginx │ └── nginx.jpg ├── redis │ └── redis.jpg ├── tool │ ├── git │ │ ├── git.jpg │ │ ├── git2.jpg │ │ └── git-ssh-keys.jpg │ ├── hexo │ │ ├── hexo.jpg │ │ ├── hexo-theme-next.jpg │ │ ├── hexo-theme-icarus.png │ │ ├── hexo-theme-next2.jpg │ │ ├── hexo-theme-icarus-preview.png │ │ └── hexo-theme-icarus-preview2.jpg │ ├── idea │ │ └── idea.jpg │ ├── maven │ │ └── maven.jpg │ └── markdown │ │ └── markdown.jpg ├── mysql │ └── mysql-thumbnail.jpg ├── springboot │ └── springboot.jpg └── springcloud │ └── springcloud.jpg ├── docs ├── todo │ ├── pig │ │ ├── pig开源版本PC端截图-首页.jpg │ │ ├── pig开源版本PC端截图-功能菜单.jpg │ │ ├── pig开源版本PC端截图-功能菜单2.jpg │ │ ├── pig开源版本PC端截图-功能菜单3.jpg │ │ └── pig.txt │ ├── erupt │ │ ├── Erupt整理笔记(2021-05-26).pdf │ │ └── erupt.txt │ ├── jeecg │ │ └── jeecg-boot开源版本PC端截图-首页.jpg │ ├── wx_ebook.md │ ├── wx_tutorial.md │ ├── redis-cluster.md │ ├── spring-boot-mybatis-pagehelper.md │ ├── redis-master-slave.md │ ├── redis-sentinel.md │ └── ruoyi │ │ └── ruoyi.txt ├── nginx │ ├── nginx-install-windows.md │ ├── nginx-windows-service.md │ ├── nginx-faq.md │ ├── nginx-fair.md │ └── nginx-ssl.md ├── linux │ ├── linux-command-reboot-shutdown.md │ ├── linux-command-scp-rzsz.md │ ├── linux-command-vi.md │ ├── linux-netstat-ps.md │ └── linux-command-tar.md ├── tool │ ├── eclipse-error.md │ ├── git-install.md │ ├── eclipse-plugins.md │ ├── nacos-install-linux.md │ ├── jdk-install-linux.md │ ├── svn-dir.md │ ├── git-ssh.md │ ├── sublime-keymap.md │ ├── idea-keymap.md │ ├── idea-plugins-easycode.md │ ├── maven-command.md │ └── docker-compose.md ├── other │ ├── mybatis-key.md │ ├── tomcat-cluster.md │ ├── poi.md │ ├── mycat-overview.md │ ├── powerjob.md │ └── mat.md ├── mq │ ├── mq-faq.md │ ├── mq-order.md │ ├── mq-not-repeatedly-consumed.md │ ├── mq-high-availability.md │ ├── mq-why.md │ └── rabbitmq-install-linux.md ├── elastic │ └── elasticsearch-overview.md ├── redis │ ├── redis-hyperloglog.md │ ├── redis-command.md │ └── redis-install.md ├── mysql │ ├── mysql-knowledge.md │ ├── mysql-procedure.md │ ├── mysql-user-privileges.md │ ├── mysql-install-windows.md │ ├── mysql-event.md │ ├── mysql-faq.md │ ├── mysql-bak-timer.md │ ├── mysql-bak-xtrabackup.md │ └── mysql-index.md ├── springboot │ ├── spring-boot-springsecurity-helloworld.md │ ├── spring-boot-staticresources.md │ ├── spring-boot-yaml.md │ ├── spring-boot-properties.md │ ├── spring-boot-helloworld.md │ ├── spring-boot-cors.md │ └── spring-boot-springsecurity-user.md ├── springcloud │ ├── spring-cloud-bus.md │ └── spring-cloud-overview.md └── hr │ └── hr-rabbitmq-reliability-consumer.md ├── _coverpage.md ├── .gitignore ├── codes └── python │ ├── data_type.py │ └── basic_syntax.py ├── _navbar.md └── lib ├── docsify └── lib │ └── plugins │ └── ga.js ├── prismjs └── components │ ├── prism-yaml.js │ ├── prism-java.js │ └── prism-sql.js ├── docsify-count └── docsify-count.js └── docsify-copy-code └── dist └── docsify-copy-code.min.js /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | www.cxy35.top -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/favicon.ico -------------------------------------------------------------------------------- /images/other/deer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/deer.jpg -------------------------------------------------------------------------------- /images/config/alipay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/alipay.jpg -------------------------------------------------------------------------------- /images/config/avatar.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/avatar.ico -------------------------------------------------------------------------------- /images/config/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/avatar.jpg -------------------------------------------------------------------------------- /images/config/cxy35.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/cxy35.ico -------------------------------------------------------------------------------- /images/config/cxy35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/cxy35.jpg -------------------------------------------------------------------------------- /images/linux/linux.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/linux/linux.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/1.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/1.png -------------------------------------------------------------------------------- /images/mq/rabbitmq/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/4.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/5.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/6.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/7.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/8.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/9.jpg -------------------------------------------------------------------------------- /images/nginx/nginx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/nginx/nginx.jpg -------------------------------------------------------------------------------- /images/other/desert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/desert.jpg -------------------------------------------------------------------------------- /images/other/flower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/flower.jpg -------------------------------------------------------------------------------- /images/other/galwba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/galwba.png -------------------------------------------------------------------------------- /images/other/plant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/plant.jpg -------------------------------------------------------------------------------- /images/redis/redis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/redis/redis.jpg -------------------------------------------------------------------------------- /images/tool/git/git.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/git/git.jpg -------------------------------------------------------------------------------- /images/tool/git/git2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/git/git2.jpg -------------------------------------------------------------------------------- /images/config/qw12op90.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/qw12op90.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/10.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/100.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/101.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/11.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/12.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/13.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/14.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/15.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/16.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/17.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/18.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/19.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/20.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/21.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/22.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/23.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/24.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/25.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/26.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/27.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/28.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/29.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/30.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/31.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/32.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/33.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/34.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/35.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/36.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/37.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/38.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/40.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/41.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/42.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/43.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/44.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/45.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/46.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/47.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/49.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/50.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/52.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/53.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/54.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/55.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/56.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/56.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/58.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/59.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/60.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/61.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/61.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/62.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/62.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/63.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/64.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/65.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/65.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/67.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/67.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/68.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/70.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/70.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/71.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/71.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/72.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/72.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/73.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/74.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/75.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/76.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/76.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/77.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/77.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/78.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/78.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/79.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/79.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/80.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/80.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/81.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/81.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/82.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/82.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/83.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/83.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/84.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/84.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/85.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/85.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/86.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/87.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/87.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/88.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/88.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/89.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/89.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/90.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/90.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/91.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/91.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/92.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/92.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/93.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/93.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/94.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/94.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/95.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/95.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/96.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/96.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/97.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/97.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/98.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/98.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/99.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/rpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/rpc.png -------------------------------------------------------------------------------- /images/other/sculpture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/sculpture.jpg -------------------------------------------------------------------------------- /images/tool/hexo/hexo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo.jpg -------------------------------------------------------------------------------- /images/tool/idea/idea.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/idea/idea.jpg -------------------------------------------------------------------------------- /images/config/avatar_150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/avatar_150.jpg -------------------------------------------------------------------------------- /images/config/avatar_258.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/avatar_258.jpg -------------------------------------------------------------------------------- /images/config/cxy35_150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/cxy35_150.jpg -------------------------------------------------------------------------------- /images/config/cxy35_258.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/cxy35_258.jpg -------------------------------------------------------------------------------- /images/config/wechatpay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/wechatpay.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/topics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/topics.png -------------------------------------------------------------------------------- /images/other/aliyun_sale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/aliyun_sale.jpg -------------------------------------------------------------------------------- /images/tool/maven/maven.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/maven/maven.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/routing.png -------------------------------------------------------------------------------- /images/other/apache/apache.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/apache/apache.jpg -------------------------------------------------------------------------------- /images/other/tomcat/tomcat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/other/tomcat/tomcat.jpg -------------------------------------------------------------------------------- /docs/todo/pig/pig开源版本PC端截图-首页.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/pig/pig开源版本PC端截图-首页.jpg -------------------------------------------------------------------------------- /images/config/qw12op90_cxy35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/qw12op90_cxy35.jpg -------------------------------------------------------------------------------- /images/mq/rabbitmq/hello-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/hello-world.png -------------------------------------------------------------------------------- /images/mq/rabbitmq/work-queues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/work-queues.png -------------------------------------------------------------------------------- /images/mysql/mysql-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mysql/mysql-thumbnail.jpg -------------------------------------------------------------------------------- /images/springboot/springboot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/springboot/springboot.jpg -------------------------------------------------------------------------------- /images/springcloud/springcloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/springcloud/springcloud.jpg -------------------------------------------------------------------------------- /images/tool/git/git-ssh-keys.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/git/git-ssh-keys.jpg -------------------------------------------------------------------------------- /images/tool/markdown/markdown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/markdown/markdown.jpg -------------------------------------------------------------------------------- /docs/todo/pig/pig开源版本PC端截图-功能菜单.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/pig/pig开源版本PC端截图-功能菜单.jpg -------------------------------------------------------------------------------- /docs/todo/pig/pig开源版本PC端截图-功能菜单2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/pig/pig开源版本PC端截图-功能菜单2.jpg -------------------------------------------------------------------------------- /docs/todo/pig/pig开源版本PC端截图-功能菜单3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/pig/pig开源版本PC端截图-功能菜单3.jpg -------------------------------------------------------------------------------- /images/config/qw12op90_business.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/config/qw12op90_business.jpg -------------------------------------------------------------------------------- /images/tool/hexo/hexo-theme-next.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo-theme-next.jpg -------------------------------------------------------------------------------- /images/tool/hexo/hexo-theme-icarus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo-theme-icarus.png -------------------------------------------------------------------------------- /images/tool/hexo/hexo-theme-next2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo-theme-next2.jpg -------------------------------------------------------------------------------- /docs/todo/erupt/Erupt整理笔记(2021-05-26).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/erupt/Erupt整理笔记(2021-05-26).pdf -------------------------------------------------------------------------------- /images/mq/rabbitmq/publish-subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/mq/rabbitmq/publish-subscribe.png -------------------------------------------------------------------------------- /docs/todo/jeecg/jeecg-boot开源版本PC端截图-首页.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/docs/todo/jeecg/jeecg-boot开源版本PC端截图-首页.jpg -------------------------------------------------------------------------------- /images/tool/hexo/hexo-theme-icarus-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo-theme-icarus-preview.png -------------------------------------------------------------------------------- /images/tool/hexo/hexo-theme-icarus-preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxy35/learning/HEAD/images/tool/hexo/hexo-theme-icarus-preview2.jpg -------------------------------------------------------------------------------- /docs/todo/erupt/erupt.txt: -------------------------------------------------------------------------------- 1 | - 表和字段的注释: 2 | - @org.hibernate.annotations.Table(appliesTo = "demo_simple", comment = "简单示例") 3 | - @Column(columnDefinition = "int(10) default 0 comment '滑动条'") 4 | - 联表查询:EruptDao 5 | -------------------------------------------------------------------------------- /docs/todo/wx_ebook.md: -------------------------------------------------------------------------------- 1 | 本文搜集各类 **最新免费电子书资源** ,希望对大家的技能提升有所帮助。大家在微信公众号 **【程序员35】** 后台回复 **ebook** ,即可获取所有资源的下载地址。 2 | 3 | 建议收藏本文,想要学习的时候回来看看。如果这里找不到你想要的资源?或者有其他资源想要共享?或者链接失效了?欢迎留言里回复。 4 | 5 | --- 6 | 7 | > 更多免费资源持续更新中,敬请期待。所有资源来自互联网,纯属个人需要在维护,如若侵权请及时联系我删除。 8 | -------------------------------------------------------------------------------- /_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](images/config/cxy35_150.jpg) 2 | 3 | ## 好好学习,天天向上 4 | 5 | ### 关注微信公众号【程序员35】,获取最新技术干货。 6 | 7 | 教程合集:Spring Boot、Spring Cloud、MySQL、Redis、MQ、Elastic、Nginx、Linux、工具、笔试面试等。 8 | 9 | 实战项目:人力资源管理系统。 10 | 11 | [GitHub](https://github.com/cxy35/learning) 12 | [Get Started](README.md) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 11 | .mvn/wrapper/maven-wrapper.jar 12 | -------------------------------------------------------------------------------- /codes/python/data_type.py: -------------------------------------------------------------------------------- 1 | counter = 100 # 整型变量 2 | miles = 1000.0 # 浮点型变量 3 | name = "runoob" # 字符串 4 | 5 | print (counter) 6 | print (miles) 7 | print (name) 8 | 9 | print('------------------------------') 10 | 11 | a = b = c = 1 12 | 13 | a, b, c = 1, 2, "runoob" 14 | 15 | a, b, c, d = 20, 5.5, True, 4+3j 16 | print(type(a), type(b), type(c), type(d)) 17 | 18 | print('------------------------------') 19 | 20 | a = 111 21 | isinstance(a, int) 22 | -------------------------------------------------------------------------------- /docs/todo/wx_tutorial.md: -------------------------------------------------------------------------------- 1 | 本文汇总所有 **最新教程合集** ,希望对大家的技能提升有所帮助。大家在微信公众号 **【程序员35】** 后台回复对应的 **关键字** ,即可获取对应的最新教程合集地址。 2 | 3 | 建议收藏本文,想要学习的时候回来看看。如果这里找不到你想要的教程?或者有其他教程想要共享?欢迎留言里回复。 4 | 5 | --- 6 | 7 | > 更多教程合集持续更新中,关键字会逐步加上,敬请期待。 8 | 9 | --- 10 | 11 | |教程合集名称|关键字| 12 | |:-|:-| 13 | |Spring Boot 教程合集|springboot| 14 | |Spring Boot 实战项目(人力资源管理系统)教程合集|hr| 15 | |Spring Cloud 教程合集|springcloud| 16 | |Nginx 教程合集|nginx| 17 | |MySQL 教程合集|mysql| 18 | |Redis 教程合集|redis| 19 | |Git 教程合集|git| 20 | |Hexo 教程合集|hexo| -------------------------------------------------------------------------------- /_navbar.md: -------------------------------------------------------------------------------- 1 | * 项目地址 2 | 3 | * [示例代码](https://github.com/cxy35/learning) 4 | * [示例代码(Spring Boot)](https://github.com/cxy35/spring-boot-samples) 5 | * [示例代码(Spring Cloud)](https://github.com/cxy35/spring-cloud-samples) 6 | * [人力资源管理系统](https://github.com/cxy35/hr) 7 | 8 | * 个人站点 9 | 10 | * [独立站点](https://www.cxy35.top) 11 | * [CSDN](https://blog.csdn.net/cxy35) 12 | * [OSCHINA](https://my.oschina.net/cxy35) 13 | * [GitHub](https://github.com/cxy35) 14 | * [Gitee](https://gitee.com/cxy35) -------------------------------------------------------------------------------- /docs/todo/redis-cluster.md: -------------------------------------------------------------------------------- 1 | cluster的出现是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。对cluster的一些理解: 2 | • cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。 3 | • 因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容 4 |   这种模式适合数据量巨大的缓存要求,当数据量不是很大使用sentinel即可。 5 | 6 | 7 | 8 | --- 9 | 10 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 11 | 12 | 13 | --- 14 | 15 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 16 | 17 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/todo/spring-boot-mybatis-pagehelper.md: -------------------------------------------------------------------------------- 1 | 学习在 Spring Boot 中整合 MyBatis 分页插件 PageHelper 2 | 3 | 4 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 5 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 6 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-dao/spring-boot-mybatis-pagehelper](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-dao/spring-boot-mybatis-pagehelper) 7 | 8 | 9 | --- 10 | 11 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 12 | 13 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/nginx/nginx-install-windows.md: -------------------------------------------------------------------------------- 1 | 手把手带你在 Windows 上安装 Nginx-1.16.0 。 2 | 3 | 4 | ```bash 5 | # 下载免安装版本的 Nginx 6 | # http://nginx.org/en/download.html 7 | # nginx-1.16.1.zip 8 | 9 | # 解压 10 | D:\nginx-1.16.0 11 | 12 | # 修改配置文件(如果需要) 13 | D:\nginx-1.16.0\conf\nginx.conf 14 | 15 | # 启动服务(如果需要,可加入系统服务) 16 | D:\nginx-1.16.0\nginx.exe 17 | ``` 18 | 19 | --- 20 | 21 | - [Nginx 教程合集](https://mp.weixin.qq.com/s/TdLki2vnjW4hKUz_BgzEHg)(微信左下方**阅读全文**可直达)。 22 | - [Nginx 参数配置优化](https://mp.weixin.qq.com/s/wS-ly5O_xSJbVzJ24_yAKQ) 23 | 24 | 25 | --- 26 | 27 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 28 | 29 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/linux/linux-command-reboot-shutdown.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Linux 常用命令-重启、关机:reboot shutdown 等。 2 | 3 | 4 | ## 重启: 5 | 6 | - reboot --> 注:立刻重启 7 | - shutdown -r now --> 注:立刻重启( root 用户使用) 8 | - shutdown -r 10 --> 注: 10 分钟后重启( root 用户使用) 9 | - shutdown -r 20:35 --> 注: 20:35 时重启( root 用户使用) 10 | 11 | 注:如果是通过 shutdown 命令设置重启的话,可以用 shutdown -c 命令取消重启。 12 | 13 | ## 关机: 14 | 15 | - halt --> 注:立刻关机 16 | - shutdown -h now --> 注:立刻关机( root 用户使用) 17 | - shutdown -h 10 --> 注: 10 分钟后关机 18 | - poweroff --> 注:立刻关机 19 | 20 | 注:如果是通过 shutdown 命令设置关机的话,可以用 shutdown -c 命令取消关机。 21 | 22 | 23 | --- 24 | 25 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 26 | 27 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/eclipse-error.md: -------------------------------------------------------------------------------- 1 | 本文记录 Eclipse 使用过程中的相关问题汇总,持续更新中。 2 | 3 | 4 | ## 1 打包时没有生成目录信息,导致 Spring 扫描时找不到 jar 包中的 Bean 5 | 6 | 原因: 7 | 8 | 打包错误,没有选中这个选项:Add directory entries 。 9 | 10 | ![](https://static.oschina.net/uploads/space/2017/1124/115344_nFcp_593078.png) 11 | 12 | 区别如下: 13 | 14 | ```bash 15 | # 选中 16 | cn/ 17 | cn/com/ 18 | cn/com/log/ 19 | cn/com/log/dao/ 20 | cn/com/log/dao/ReadLogInfoDao.class 21 | cn/com/log/dao/LogInfoDao.class 22 | 23 | # 不选中(错误) 24 | cn/com/log/dao/ReadLogInfoDao.class 25 | cn/com/log/dao/LogInfoDao.class 26 | ``` 27 | 28 | 解决:打包时需要选中上述选项。 29 | 30 | 31 | --- 32 | 33 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 34 | 35 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/git-install.md: -------------------------------------------------------------------------------- 1 | 手把手带你安装 Git 。 2 | 3 | 4 | ## Windows 5 | 6 | windows 安装 Git 整体上来说有两种解决方案: 7 | 8 | 1. 安装 [Cygwin](http://cygwin.com/) 用来模拟 Linux 运行环境,但是 Cygwin 配置非常麻烦,容易出错,所以一般不推荐这种方式。 9 | 2. 安装独立的 Git,也就是 [msysGit](https://git-for-windows.github.io/),下载 `Git-2.23.0-64-bit.exe` ,安装成功后,在你的开始菜单中找到 Git Bash,或者鼠标右键 Git Bash Here 可打开命令行。 10 | 11 | ```bash 12 | $ git --version 13 | git version 2.23.0.windows.1 14 | ``` 15 | 16 | ## Ubuntu 17 | 18 | ```bash 19 | sudo apt-get install git 20 | ``` 21 | 22 | --- 23 | 24 | - [Git 教程合集](https://mp.weixin.qq.com/s/S_wAUhlN1hqTjl4CwFS19Q) 25 | 26 | 27 | --- 28 | 29 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 30 | 31 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/eclipse-plugins.md: -------------------------------------------------------------------------------- 1 | Eclipse 常用插件在线安装地址。 2 | 3 | 4 | - Maven2: [http://m2eclipse.sonatype.org/sites/m2e](http://m2eclipse.sonatype.org/sites/m2e) 5 | - Maven-wtp: [http://m2eclipse.sonatype.org/sites/m2e-extras](http://m2eclipse.sonatype.org/sites/m2e-extras) 6 | - Subclipse: [http://subclipse.tigris.org/update_1.8.x](http://subclipse.tigris.org/update_1.8.x)(支持 Subversion 1.7.x ) 7 | - Android(Eclipse ADT):[https://dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/) 8 | - FreeMarker:[http://download.jboss.org/jbosstools/updates/development/helios](http://download.jboss.org/jbosstools/updates/development/helios)(最后一个为 Eclipse 的版本) 9 | 10 | 11 | 12 | 13 | --- 14 | 15 | 扫码关注微信公众号 程序员35 ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 16 | 17 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/nacos-install-linux.md: -------------------------------------------------------------------------------- 1 | 手把手带你在 Linux 上安装 `Nacos` 。 2 | 3 | 4 | ## 1 准备工作 5 | 6 | - 访问 [https://github.com/alibaba/nacos/releases](https://github.com/alibaba/nacos/releases) 下载对应**稳定版**的安装包,如:`nacos-server-2.1.0.tar.gz`。 7 | - 上传到服务器的 `/usr/local/mydata/temp` 目录下。如果没有,则手动新建。 8 | 9 | ## 2 安装 10 | 11 | ### 2.1 解压 12 | 13 | ```bash 14 | # 其中 nacos-server-2.1.0.tar.gz 换成实际的名称 15 | cd /usr/local/mydata/temp 16 | tar -xvzf nacos-server-2.1.0.tar.gz -C /usr/local/mydata/soft 17 | ``` 18 | 19 | ### 2.2 启动 20 | 21 | ```bash 22 | # 切换到 Nacos 安装目录 23 | cd /usr/local/mydata/soft/nacos 24 | # standalone 代表单机模式运行,非集群模式 25 | sh /usr/local/mydata/soft/nacos/bin/startup.sh -m standalone 26 | # ps -ef|grep nacos 27 | ``` 28 | 29 | ### 2.4 验证 30 | 31 | Nacos 启动成功后,访问 [http://127.0.0.1:8848/nacos](http://127.0.0.1:8848/nacos) 就能看到后台页面。如果有登录页面,登录的默认用户名/密码都是 `nacos` 。 32 | 33 | 34 | --- 35 | 36 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 37 | 38 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/docsify/lib/plugins/ga.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js 3 | function appendScript() { 4 | var script = document.createElement('script'); 5 | script.async = true; 6 | script.src = 'https://www.google-analytics.com/analytics.js'; 7 | document.body.appendChild(script); 8 | } 9 | 10 | function init(id) { 11 | appendScript(); 12 | window.ga = 13 | window.ga || 14 | function () { 15 | (window.ga.q = window.ga.q || []).push(arguments); 16 | }; 17 | window.ga.l = Number(new Date()); 18 | window.ga('create', id, 'auto'); 19 | } 20 | 21 | function collect() { 22 | if (!window.ga) { 23 | init($docsify.ga); 24 | } 25 | 26 | window.ga('set', 'page', location.hash); 27 | window.ga('send', 'pageview'); 28 | } 29 | 30 | var install = function (hook) { 31 | if (!$docsify.ga) { 32 | console.error('[Docsify] ga is required.'); 33 | return 34 | } 35 | 36 | hook.beforeEach(collect); 37 | }; 38 | 39 | $docsify.plugins = [].concat(install, $docsify.plugins); 40 | 41 | }()); 42 | -------------------------------------------------------------------------------- /docs/linux/linux-command-scp-rzsz.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Linux 常用命令-文件传输:scp rz sz 等。 2 | 3 | 4 | ## ssh 命令行传输:scp 5 | 6 | ```bash 7 | # 文件上传,默认 22 端口 8 | scp /usr/local/test.txt root@192.168.1.53:/usr/local 9 | 10 | # 文件上传,指定 8122 端口 11 | # test.txt 会被上传到 local 目录下( /usr/local/test.txt ) 12 | scp -P 8122 /usr/local/test.txt root@192.168.1.53:/usr/local 13 | 14 | # 目录上传 15 | # test 目录会被上传到 local 目录下( /usr/local/test ) 16 | scp -r /usr/local/test root@192.168.1.53:/usr/local 17 | 18 | # 文件下载 19 | scp root@192.168.1.53:/usr/local/test.txt /usr/local 20 | 21 | # 目录下载 22 | scp -r root@192.168.1.53:/usr/local/test /usr/local 23 | ``` 24 | 25 | ## xshell 结合 lrzsz 工具传输:rz sz 26 | 27 | ```bash 28 | # 检查是否安装过工具 lrzsz 29 | rpm -qa |grep lrzsz 30 | 31 | # 安装工具 lrzsz 32 | yum -y install lrzsz 33 | 34 | # 文件上传,会打开本地选择文件对话框 35 | rz 36 | 37 | # 文件下载,会弹出选择本地保存文件对话框 38 | sz 39 | 40 | # xshell 中直接拖拽文件到窗口中也可以上传。 Alt+P 打开属性框,打开[文件传输]可以调整传输的一些属性 41 | ``` 42 | 43 | 44 | --- 45 | 46 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 47 | 48 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/todo/redis-master-slave.md: -------------------------------------------------------------------------------- 1 | 首先谈谈我对主从模式的必要性: 2 | • 主从模式的一个作用是备份数据,这样当一个节点损坏(指不可恢复的硬件损坏)时,数据因为有备份,可以方便恢复。 3 | • 另一个作用是负载均衡,所有客户端都访问一个节点肯定会影响Redis工作效率,有了主从以后,查询操作就可以通过查询从节点来完成。 4 |   对主从模式必须的理解(结论已经验证过,可以自行验证): 5 | 6 | 1. 一个Master可以有多个Slaves 7 | 2. 默认配置下,master节点可以进行读和写,slave节点只能进行读操作,写操作被禁止 8 | 3. 不要修改配置让slave节点支持写操作,没有意义,原因一,写入的数据不会被同步到其他节点;原因二,当master节点修改同一条数据后,slave节点的数据会被覆盖掉 9 | 4. slave节点挂了不影响其他slave节点的读和master节点的读和写,重新启动后会将数据从master节点同步过来 10 | 5. master节点挂了以后,不影响slave节点的读,Redis将不再提供写服务,master节点启动后Redis将重新对外提供写服务。 11 | 6. master节点挂了以后,不会slave节点重新选一个master 12 | 13 |   对有密码的情况说明一下,当master节点设置密码时: 14 | • 客户端访问master需要密码 15 | • 启动slave需要密码,在配置中进行配置即可 16 | • 客户端访问slave不需要密码 17 | 18 | 1.1 主从节点的缺点 19 |   主从模式的缺点其实从上面的描述中可以得出: 20 | • master节点挂了以后,redis就不能对外提供写服务了,因为剩下的slave不能成为master 21 |   这个缺点影响是很大的,尤其是对生产环境来说,是一刻都不能停止服务的,所以一般的生产坏境是不会单单只有主从模式的。所以有了下面的sentinel模式。 22 | 23 | 24 | 25 | --- 26 | 27 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 28 | 29 | 30 | --- 31 | 32 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 33 | 34 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/mybatis-key.md: -------------------------------------------------------------------------------- 1 | 两种方式实现 MyBatis 主键回填。 2 | 3 | 4 | ## 方式一: useGeneratedKeys 5 | 6 | ```xml 7 | 8 | insert into t_user (username, phone) values (#{username}, #{phone}); 9 | 10 | ``` 11 | 12 | 通过在 `insert` 节点上增加 `useGeneratedKeys` 和 `keyProperty` 属性来实现,这样通过传入一个对象执行插入操作,插入完成后,这个对象的 id 就会被自动赋值,**推荐使用这种方式**。 13 | 14 | ## 方式二: LAST_INSERT_ID 15 | 16 | ```xml 17 | 18 | 19 | SELECT LAST_INSERT_ID() 20 | 21 | insert into t_user (username, phone) values (#{username}, #{phone}); 22 | 23 | ``` 24 | 25 | 通过在 `insert` 节点内增加 `selectKey` 节点来实现,`LAST_INSERT_ID()` 函数是 MySQL 自带的,可以查询刚刚插入的 id 。其实这种方式更加灵活,因为 selectKey 节点中的 SQL 可以控制在插入之前/之后执行(通过设置节点的 Order 属性为 AFTER 或者 BEFORE 可以实现)。 26 | 27 | 注意:这种方式也要设置 keyProperty 来指定将查询到的数据绑定到哪个属性上。 28 | 29 | 30 | --- 31 | 32 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 33 | 34 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/todo/redis-sentinel.md: -------------------------------------------------------------------------------- 1 | sentinel的中文含义是哨兵、守卫。也就是说既然主从模式中,当master节点挂了以后,slave节点不能主动选举一个master节点出来,那么我就安排一个或多个sentinel来做这件事,当sentinel发现master节点挂了以后,sentinel就会从slave中重新选举一个master。 2 | 3 |   对sentinel模式的理解: 4 | • sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义 5 | • 当master节点挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master 6 | • 当master节点重新启动后,它将不再是master而是做为slave接收新的master节点的同步数据 7 | • sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群 8 | • 当主从模式配置密码时,sentinel也会同步将配置信息修改到配置文件中,不许要担心。 9 | • 一个sentinel或sentinel集群可以管理多个主从Redis。 10 | • sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了 11 | • sentinel监控的Redis集群都会定义一个master名字,这个名字代表Redis集群的master Redis。 12 | 13 |   当使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。 14 |   sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中,就是下面要讲的。 15 | 16 | 17 | 18 | --- 19 | 20 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 21 | 22 | 23 | --- 24 | 25 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 26 | 27 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /codes/python/basic_syntax.py: -------------------------------------------------------------------------------- 1 | # 1.基础语法 2 | 3 | """ 4 | 多行注释 5 | """ 6 | 7 | print('------------------------------') 8 | 9 | str='123456789' 10 | 11 | print(str) # 输出字符串 12 | print(str[0:-1]) # 输出第一个到倒数第二个的所有字符 13 | print(str[0]) # 输出字符串第一个字符 14 | print(str[2:5]) # 输出从第三个开始到第六个的字符(不包含) 15 | print(str[2:]) # 输出从第三个开始后的所有字符 16 | print(str[1:5:2]) # 输出从第二个开始到第五个且每隔一个的字符(步长为2) 17 | print(str * 2) # 输出字符串两次 18 | print(str + '你好') # 连接字符串 19 | 20 | print('------------------------------') 21 | 22 | print('hello\nrunoob') # 使用反斜杠(\)+n转义特殊字符 23 | print(r'hello\nrunoob') # 在字符串前面添加一个 r,表示原始字符串,不会发生转义 24 | 25 | print('------------------------------') 26 | 27 | # input("\n\n按下 enter 键后退出。") 28 | 29 | x="a" 30 | y="b" 31 | # 换行输出 32 | print( x ) 33 | print( y ) 34 | 35 | print('------------------------------') 36 | # 不换行输出 37 | print( x, end=" " ) 38 | print( y, end=" " ) 39 | print() 40 | 41 | print('================Python import mode==========================') 42 | import sys 43 | print ('命令行参数为:') 44 | for i in sys.argv: 45 | print (i) 46 | print ('\n python 路径为',sys.path) 47 | 48 | print('================python from import===================================') 49 | from sys import argv,path # 导入特定的成员 50 | print('path:',path) # 因为已经导入path成员,所以此处引用时不需要加sys.path -------------------------------------------------------------------------------- /docs/tool/jdk-install-linux.md: -------------------------------------------------------------------------------- 1 | 手把手带你在 Linux 上安装 `jdk` 。 2 | 3 | 4 | ## 1 准备工作 5 | 6 | - 访问 [https://www.oracle.com/java/technologies/downloads/](https://www.oracle.com/java/technologies/downloads/) 下载对应**稳定版**的安装包,如:`jdk-8u321-linux-x64.tar.gz`。 7 | - 上传到服务器的 `/usr/local/mydata/temp` 目录下。如果没有,则手动新建。 8 | 9 | ## 2 安装 10 | 11 | ### 2.1 解压 12 | 13 | ```bash 14 | # 其中 jdk-8u321-linux-x64.tar.gz 换成实际的名称 15 | cd /usr/local/mydata/temp 16 | tar -xvzf jdk-8u321-linux-x64.tar.gz -C /usr/local/mydata/soft 17 | ``` 18 | 19 | ### 2.2 重命名 20 | 21 | ```bash 22 | # 其中 jdk1.8.0_321 换成实际的名称 23 | cd /usr/local/mydata/soft 24 | mv jdk1.8.0_321 jdk 25 | ``` 26 | 27 | ### 2.3 配置环境变量 28 | 29 | 执行:`vi /etc/profile` 修改配置。 30 | 31 | ```bash 32 | # 在文件末尾添加下面内容并保存成功 33 | JAVA_HOME=/usr/local/mydata/soft/jdk 34 | PATH=$PATH:$JAVA_HOME/bin 35 | CLASSPATH=$CLASSPATH:$JAVA_HOME/lib 36 | export JAVA_HOME PATH CLASSPATH 37 | ``` 38 | 39 | 执行:`source /etc/profile` 使配置生效。 40 | 41 | ### 2.4 验证 42 | 43 | 执行:`java -version` 44 | 45 | ```bash 46 | # 显示如下信息表示验证通过 47 | java version "1.8.0_321" 48 | Java(TM) SE Runtime Environment (build 1.8.0_321-b07) 49 | Java HotSpot(TM) 64-Bit Server VM (build 25.321-b07, mixed mode) 50 | ``` 51 | 52 | 53 | --- 54 | 55 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 56 | 57 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/svn-dir.md: -------------------------------------------------------------------------------- 1 | SVN 目录使用规范。 2 | 3 | 4 | ## 规范 1(推荐) 5 | 6 | |目录名称|说明| 7 | |:-|:-| 8 | |trunk|trunk 是树干,主分支,日常开发进行的地方。| 9 | |branches|branches 是分支。是用来做并行开发的,这里的并行是指和 trunk 进行比较,完成后一般会被合并到 trunk 中。一般有 3 类:【1.】一些阶段性的 release 版本,这些版本是可以继续进行开发和维护的,如 2.1 或 2.x ( 2 系列版本的最新代码)。【2.】为不同用户客制化的版本,也可以放在分支中进行开发,如 2.2_dev 。【3.】某个版本的bug修复,如: 2.1_bugfix 。| 10 | |tags|tags 是标记,一般是只读的,这里存储阶段性的发布版本,只是作为一个里程碑的版本进行存档,如: 2.1 ,2.1.1 。| 11 | 12 | ## 规范 2 13 | 14 | |目录名称|稳定程度|权限|说明| 15 | |:-|:-|:-|:-| 16 | |branches|开发分支,不稳定|开发 team 有权限|有开发任务时,从 trunk 打分支到 branches ,分支命名以日期为前缀,如: 20170101 ,可再加上_本次分支主要内容,如: _monitor 。(如果 trunk 分支在测试且证明极度不稳定,想取稳定分支,从 tags 取)。开发且自测完成时,由研发 Leader 合并到主干 trunk ,测试从 trunk 发包进行测试。一般有 3 类:1.】准备发布的分支(进行生产环境的测试、准备) Release Branch ,如 BUG-1.0_235 ( copy from tag/tag_release_1.0 , bug 版本号为235)。【2.】Bug 修复的分支(进行某编号的 bug 修复) Bug fix branch ,如 RB-1.1 ( 1.1 版本的 Release Branch )。【3.】新技术实验性分支(将某个新技术引进项目) Experimental branch ,如 TRY-1.0_PHP7 ( copy from tag/tag_release_1.0 ,PHP7 实验技术)。这些都要根据需要最终 merge 到 trunk 里面。| 17 | |trunk|主干分支,趋于稳定|开发 Leader 有权限|最新趋于稳定版本代码存放地。开发 Leader 有权限从开发分支 merge 代码到主干,然后质量部进行测试,测试通过由运维部打上线分支到 tags 。研发 leader 要控制 trunk 的时序性。(也就是说尽量避免一个 brances 合并到 trunk 进行测试之后,在没有完成测试前又合并一个分支,导致测试返工。)| 18 | |tags|上线分支,稳定|运维有权限|方便回滚和记录。以版本号命名,如 1.1、1.2 。| 19 | 20 | 21 | --- 22 | 23 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 24 | 25 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/tomcat-cluster.md: -------------------------------------------------------------------------------- 1 | 多种方式实现 Tomcat 集群。 2 | 3 | 4 | ## 1 概述 5 | 6 | ### 1.1 集群能带来什么 7 | 8 | - 提高服务的性能,例如计算处理能力、并发能力等,以及实现服务的高可用性。 9 | - 提供项目架构的横向扩展能力,增加集群中的机器就能提高集群的性能。 10 | - 提升对静态文件的处理性能。 11 | - 利用 Web 服务器来做负载均衡以及容错。 12 | - 无缝的升级应用程序。 13 | 14 | ### 1.2 集群实现方式 15 | 16 | - Tomcat 集群的实现方式有多种,最简单的就是通过 Nginx 负载进行请求转发来实现。 17 | - session 共享问题。 18 | 19 | ### 1.3 集群解决方案 20 | 21 | - 采用 Nginx 中的 ip hash policy 来保持某个ip始终连接在某一个机器上 22 | - 优点:可以不改变现有的技术架构,直接实现横向扩展,省事。但是缺陷也很明显,在实际的生产环境中,极少使用这种方式 23 | - 缺点:1.单止服务器请求(负载)不均衡,这是完全依赖 ip hash 的结果。2.客户机 ip 动态变化频繁的情况下,无法进行服务,因为可能每次的 ip hash 都不一样,就无法始终保持只连接在同一台机器上。 24 | - 采用 redis 或 memchche 等 nosql 数据库,实现一个缓存 session 的服务器,当请求过来的时候,所有的 Tomcat Server 都统一往这个服务器里读取 session 信息。这是企业中比较常用的一种解决方案,所以大致的 Tomcat 集群的架构图如下: 25 | 26 | ![](https://oscimg.oschina.net/oscnet/66e6b896677b6165798c7844a654c38a434.jpg) 27 | 28 | ## 2 Apache 29 | 30 | ### 2.1 mod_jk 组件(老版本) 31 | 32 | 具体配置见:[Apache 实现 Tomcat 集群 - mod_jk(老版本)](https://mp.weixin.qq.com/s/TGgO7suN_F3I_PqoVAN9ww) 33 | 34 | ### 2.2 mod_proxy 组件(新版本) 35 | 36 | 具体配置见:[Apache 实现 Tomcat 集群 - mod_proxy(新版本)](https://mp.weixin.qq.com/s/wDrhTHxf0mW72DgJFkSftA) 37 | 38 | ## 3 Nginx(推荐) 39 | 40 | 具体配置见:[Nginx 实现 Tomcat 集群(推荐)](https://mp.weixin.qq.com/s/mq54xYiVd76EJFruHGjtbQ) 41 | 42 | 43 | --- 44 | 45 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 46 | 47 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/git-ssh.md: -------------------------------------------------------------------------------- 1 | GitHub 支持 HTTPS 和 SSH 两种协议。使用 HTTPS 协议时,每次提交都要求输入用户名和密码,显得有点麻烦。本文介绍如何通过配置 SSH keys 实现愉快的提交。 2 | 3 | 4 | 配置 SSH keys 的原理很简单,采用非对称加密方式生成公钥和私钥,公钥告诉 GitHub ,私钥留在自己电脑上(私钥不可泄露),当我们向 GitHub 上提交数据时,GitHub 会用我们留给它的公钥加密一段消息返回给我们的电脑,如果我们能够用私钥解密成功,说明是合法的用户,这样就避免我们输入用户名密码了。大致的原理就是这样,现在很多免登录的系统都采用了这种方式,比如 Hadoop 免登录配置也是这样。 5 | 6 | ## 1 查看本地是否已有 SSH keys 7 | 8 | 查看当前用户目录下是否有 `.ssh` 文件夹,如果有就跳过第 2 和 3 步。 9 | 10 | ```bash 11 | $ ls -la ~/.ssh 12 | total 32 13 | drwxr-xr-x 1 Administrator 197121 0 八月 27 15:29 ./ 14 | drwxr-xr-x 1 Administrator 197121 0 二月 10 16:44 ../ 15 | -rw-r--r-- 1 Administrator 197121 3381 八月 27 15:21 id_rsa 16 | -rw-r--r-- 1 Administrator 197121 742 八月 27 15:21 id_rsa.pub 17 | -rw-r--r-- 1 Administrator 197121 1593 九月 5 17:05 known_hosts 18 | ``` 19 | 20 | ## 2 生成 SSH 指纹 21 | 22 | ```bash 23 | ssh-keygen -t rsa -b 4096 -C "你的邮箱地址" 24 | ``` 25 | 26 | ## 3 添加 SSH 到 ssh-agent 中 27 | 28 | ```bash 29 | eval "$(ssh-agent -s)" 30 | ``` 31 | 32 | 执行完上述语句之后,我们当前用户目录下已经有了一个名为 `.ssh` 的隐藏文件夹了,打开这个目录,会发现有一个名为 `id_rsa.pub` 的文件,这就是我们一会要使用的公钥文件。 33 | 34 | ## 4 将公钥告诉 GitHub 35 | 登录 GitHub ,点击右上角的向下的箭头,选择 Settings ,在新打开的页面中左边侧栏选择 SSH and GPG keys ,再右边选择 New SSH key,输入 Title 和 Key 。 36 | 37 | Title 的值建议能标识出哪台设备,如你的电脑型号、操作系统名称等信息。Key 的值为上述 `id_rsa.pub` 文件中的内容。 38 | 39 | --- 40 | 41 | - [Git 教程合集](https://mp.weixin.qq.com/s/S_wAUhlN1hqTjl4CwFS19Q) 42 | 43 | 44 | --- 45 | 46 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 47 | 48 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/poi.md: -------------------------------------------------------------------------------- 1 | 通过本文认识 POI ,并学习 Excel/Word/... 等类型文档的操作。 2 | 3 | 4 | ## 1 POI 简介 5 | 6 | POI 是 Apache 下的 Jakata 项目的一个子项目,主要用于提供 java 操作 Microsoft Office 办公套件如 Excel,Word,Powerpoint 等文件的 API 。 7 | 8 | 微软的 Office 办公软件在企业的日常办公中占据着重要的地位,人们已经非常熟悉 Office 的使用。在我们开发的应用系统中,常常需要将数据导出到 Excel 文件中,或者Word 文件中进行打印。比如移动的话费查询系统中就提供了将话费清单导入到 excel 表格中的功能。这样在 web 应用中,我们在浏览器中看到的数据可以被导出到 Excel 中了。 9 | 10 | - Excel文件: xls 格式文件对应 POI API 为 HSSF。xlsx 格式为 office 2007 的文件格式,POI 中对应的 API 为XSSF。 11 | - Word文件:doc 格式文件对应的 POI API 为 HWPF 。 docx 格式为 XWPF 。 12 | - PowerPoint文件:ppt 格式对应的 POI API 为 HSLF 。 pptx 格式为 XSLF 。 13 | - Outlook文件:对应的 API 为 HSMF 。 14 | - Visio文件:对应的 API 为 HDGF 。 15 | - Publisher文件:对应的 API 为 HPBF 。 16 | 17 | 下载地址:[http://poi.apache.org/download.html](http://poi.apache.org/download.html) 18 | 19 | ## 2 Excel 操作手册 20 | 21 | 一个 Excel 文档称为工作簿(worksheet),一个工作簿包含多个工作表(sheet),每个工作表看起来像一张二维表格,由很多行(row)组成,每行由多个单元格组成(cell)。 22 | 23 | |POI HSSF API 中的类|Excel 结构| 24 | |:-|:-| 25 | |HSSFWorkbook|工作簿| 26 | |HSSFSheet|工作表| 27 | |HSSFRow|行| 28 | |HSSFCell|单元格| 29 | |HSSFCellStyle|单元格样式| 30 | |HSSFFont|字体| 31 | |HSSFDataFormat|单元格日期格式| 32 | |HSSFHeader|sheet 的页眉| 33 | |HSSFFooter|sheet 的页脚| 34 | |HSSFDateUtil|日期| 35 | |HSSFPrintSetup|打印| 36 | |HSSFErrorConstants|错误信息表| 37 | 38 | ## 3 Word 操作手册 39 | 40 | ## 4 PowerPoint 操作手册 41 | 42 | ## 5 Outlook 操作手册 43 | 44 | ## 6 Visio 操作手册 45 | 46 | ## 7 Publisher 操作手册 47 | 48 | 49 | --- 50 | 51 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 52 | 53 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/prismjs/components/prism-yaml.js: -------------------------------------------------------------------------------- 1 | Prism.languages.yaml = { 2 | 'scalar': { 3 | pattern: /([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/, 4 | lookbehind: true, 5 | alias: 'string' 6 | }, 7 | 'comment': /#.*/, 8 | 'key': { 9 | pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/, 10 | lookbehind: true, 11 | alias: 'atrule' 12 | }, 13 | 'directive': { 14 | pattern: /(^[ \t]*)%.+/m, 15 | lookbehind: true, 16 | alias: 'important' 17 | }, 18 | 'datetime': { 19 | pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?)(?=[ \t]*(?:$|,|]|}))/m, 20 | lookbehind: true, 21 | alias: 'number' 22 | }, 23 | 'boolean': { 24 | pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im, 25 | lookbehind: true, 26 | alias: 'important' 27 | }, 28 | 'null': { 29 | pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im, 30 | lookbehind: true, 31 | alias: 'important' 32 | }, 33 | 'string': { 34 | pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}|\s*#))/m, 35 | lookbehind: true, 36 | greedy: true 37 | }, 38 | 'number': { 39 | pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im, 40 | lookbehind: true 41 | }, 42 | 'tag': /![^\s]+/, 43 | 'important': /[&*][\w]+/, 44 | 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./ 45 | }; 46 | 47 | Prism.languages.yml = Prism.languages.yaml; -------------------------------------------------------------------------------- /docs/mq/mq-faq.md: -------------------------------------------------------------------------------- 1 | 消息队列常见问题解答。 2 | 3 | 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决? 4 | 5 | ## 1 大量消息在 mq 里积压了几个小时了还没解决 6 | 7 | 几千万条数据在 MQ 里积压了七八个小时,从下午 4 点多,积压到了晚上 11 点多。这个是我们真实遇到过的一个场景,确实是线上故障了,这个时候要不然就是修复 consumer 的问题,让它恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不能在面试的时候说吧。 8 | 9 | 一个消费者一秒是 1000 条,一秒 3 个消费者是 3000 条,一分钟就是 18 万条。所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概 1 小时的时间才能恢复过来。 10 | 11 | 一般这个时候,只能临时紧急扩容了,具体操作步骤和思路如下: 12 | 13 | 1. 先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉。 14 | 2. 新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。 15 | 3. 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,**消费之后不做耗时的处理**,直接均匀轮询写入临时建立好的 10 倍数量的 queue。 16 | 4. 接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。 17 | 5. 等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。 18 | 19 | ## 2 mq 中的消息过期失效了 20 | 21 | 假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是**大量的数据会直接搞丢**。 22 | 23 | 这个情况下,就不是说要增加 consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是**批量重导**,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。 24 | 25 | 假设 1 万个订单积压在 mq 里面,没有处理,其中 1000 个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq 里去再补一次。 26 | 27 | ## 3 mq 都快写满了 28 | 29 | 如果消息积压在 mq 里,你很长时间都没有处理掉,此时导致 mq 都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。 30 | 31 | --- 32 | 33 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 34 | 35 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/linux/linux-command-vi.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Linux 常用命令-文件内容编辑:vi 。 2 | 3 | 4 | vi 有 2 种模式:命令模式、插入模式,用 Esc 或 i 等实现模式切换。 5 | 6 | ## 文件的创建 7 | 8 | - vi 1.txt 9 | 10 | ## 文件的保存和退出 11 | 12 | - :w  [newFileName]  保存 13 | - :wq!  [newFileName]  保存退出 14 | - :q!  不保存退出 15 | 16 | ## 光标移动 17 | 18 | - gg  移动至文档开始 19 | - GG  移动到文档尾部 20 | - ctrl + f  向前翻页(同 PgDn ) 21 | - ctrl + b  向后翻页(同 PgUp ) 22 | - j  向下移动一行(同方向键) 23 | - k  向上移动一行(同方向键) 24 | - h  向左移动一个字符(同方向键) 25 | - l  向右移动一个字符(同方向键) 26 | 27 | ## 复制/粘帖 28 | 29 | - yy  复制当前行 30 | - #yy  复制多行,# 用数字表示,比如 3yy 表示复制3行 31 | - p  在光标之后粘帖 32 | - shift + p  在光标之前粘帖 33 | 34 | ## 删除 35 | 36 | - x  删除1个字符 37 | - #x  删除多个字符,# 用数字表示,比如 3x 表示删除 3 个字符 38 | - dw  删除1个单词 39 | - #dw  删除多个单词,# 用数字表示,比如 3dw 表示删除 3 个单词 40 | - dd  删除1行 41 | - #dd  删除多行,# 用数字表示,比如 3dd 表示删除光标行及光标的下两行 42 | - d$  删除光标到行尾的内容 43 | 44 | ## 撤消 45 | 46 | - u  撤消修改或删除操作 47 | 48 | ## 插入模式 49 | 50 | - i  在光标之前插入,然后进入插入模式 51 | - I  在光标所在行的行首插入,然后进入插入模式 52 | - a  在光标之后插入,然后进入插入模式 53 | - A  在光标所在行的行末插入,然后进入插入模式 54 | - o  在光标所在的行的下面插入一行,然后进入插入模式 55 | - O  在光标所在的行的上面插入一行,然后进入插入模式 56 | - s  删除光标后的一个字符,然后进入插入模式 57 | - S  删除光标所在行的所有字符,然后进入插入模式 58 | 59 | ## 查找 60 | 61 | - /字符串  注:正向查找,按 n 键往下,按 shift + n 键往上,查找下一个符合条件的地方 62 | - ?字符串  注:反向查找,按 shift + n 键往下,按 n 键往上,查找下一个符合条件的地方。 63 | 64 | ## 替换 65 | 66 | - :s /字符串1/字符串2/g  注:替换**当前行**中的所有字符串1为字符串2。如果没有 /g,则只替换**当前行**中的第一个 67 | - :%s /字符串1/字符串2/g  注:替换**每一行**中的所有字符串1为字符串2。如果没有 /g,则只替换**每一行**中的第一个 68 | - :#,# s /字符串1/字符串2/g  注:# 表示数字,表示从多少行到多少行,把字符串1替换成字符串2。 69 | 70 | 71 | --- 72 | 73 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 74 | 75 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mq/mq-order.md: -------------------------------------------------------------------------------- 1 | 如何保证消息的顺序性? 2 | 3 | 举个例子,你在 mysql 里增删改一条数据,对应出来了增删改 3 条 `binlog` 日志,接着这三条 `binlog` 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你楞是换了顺序给执行成删除、修改、增加,不全错了么。 4 | 5 | 本来这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。 6 | 7 | ## 1 RabbitMQ 8 | 9 | - 先看看顺序会错乱的场景: 10 | 11 | 一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者2先执行完操作,把 data2 存入数据库,然后是 data1/data3。这不明显乱了。 12 | 13 | ![](https://oscimg.oschina.net/oscnet/up-e57efcf34ab29c3779c7c562e1be5901d87.png) 14 | 15 | - 解决方案: 16 | 17 | 拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。 18 | 19 | ![](https://oscimg.oschina.net/oscnet/up-c6f8f4fba37228e679d456cd5ad0e0805c3.png) 20 | 21 | ## 2 Kafka 22 | 23 | - 先看看顺序会错乱的场景: 24 | 25 | 比如说我们建了一个 topic,有三个 partition。生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。 26 | 27 | 消费者从 partition 中取出来数据的时候,也一定是有顺序的。到这里,顺序还是 ok 的,没有错乱。接着,我们在消费者里可能会搞**多个线程来并发处理消息**。因为如果消费者是单线程消费处理,而处理比较耗时的话,比如处理一条消息耗时几十 ms,那么 1 秒钟只能处理几十条消息,这吞吐量太低了。而多个线程并发跑的话,顺序可能就乱掉了。 28 | 29 | ![](https://oscimg.oschina.net/oscnet/up-3fcdeafff0e75a37d763fa0738d4ad16522.png) 30 | 31 | - 解决方案: 32 | 33 | * 一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。 34 | * 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。 35 | 36 | ![](https://oscimg.oschina.net/oscnet/up-b2abb301cfead3fe8ad2cf2749c982d0137.png) 37 | 38 | --- 39 | 40 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 41 | 42 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/todo/ruoyi/ruoyi.txt: -------------------------------------------------------------------------------- 1 | com.ruoyi 2 | ├── ruoyi-ui // 前端框架 [80] 3 | ├── ruoyi-gateway // 网关模块 [8080] 4 | ├── ruoyi-auth // 认证中心 [9200] 5 | ├── ruoyi-api // 接口模块 6 | │ └── ruoyi-api-system // 系统接口 7 | ├── ruoyi-common // 通用模块 8 | │ └── ruoyi-common-core // 核心模块 9 | │ └── ruoyi-common-datascope // 权限范围 10 | │ └── ruoyi-common-datasource // 多数据源 11 | │ └── ruoyi-common-log // 日志记录 12 | │ └── ruoyi-common-redis // 缓存服务 13 | │ └── ruoyi-common-security // 安全模块 14 | │ └── ruoyi-common-swagger // 系统接口 15 | ├── ruoyi-modules // 业务模块 16 | │ └── ruoyi-system // 系统模块 [9201] 17 | │ └── ruoyi-gen // 代码生成 [9202] 18 | │ └── ruoyi-job // 定时任务 [9203] 19 | │ └── ruoyi-file // 文件服务 [9300] 20 | ├── ruoyi-visual // 图形化管理模块 21 | │ └── ruoyi-visual-monitor // 监控中心 [9100] 22 | ├──pom.xml // 公共依赖 23 | 24 | 25 | ======================================================== 26 | 27 | 28 | 依赖关系: 29 | ruoyi-common-security -> ruoyi-api-system/ruoyi-common-redis 30 | ruoyi-common-datascope -> ruoyi-common-security 31 | ruoyi-gateway -> ruoyi-common-redis 32 | ruoyi-auth -> ruoyi-common-security 33 | ruoyi-api-system -> ruoyi-common-core 34 | ruoyi-system -> ruoyi-common-datascope/ruoyi-common-datasource/ruoyi-common-log/ruoyi-common-swagger 35 | 36 | 37 | 核心功能: 38 | ruoyi-auth:token相关接口、登录登出接口 39 | ruoyi-api-system:通用实体类、通用远程调用接口 40 | 41 | 42 | 版本待完善的功能: 43 | -------------------------------------------------------------------------------- /docs/elastic/elasticsearch-overview.md: -------------------------------------------------------------------------------- 1 | Elasticsearch 概述。 2 | 3 | 4 | ## 1 简介 5 | 6 | Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式的全文搜索引擎,基于 restful web 接口。Elasticsearch 是用 Java 语言开发的,基于 Apache 协议的开源项目,是目前最受欢迎的企业搜索引擎。Elasticsearch 广泛运用于云计算中,能够达到实时搜索,具有稳定,可靠,快速的特点。 7 | 8 | ## 2 基本概念 9 | 10 | - `Near Realtime`(近实时):Elasticsearch 是一个近乎实时的搜索平台,这意味着从索引文档到可搜索文档之间只有一个轻微的延迟(通常是一秒钟)。 11 | - Cluster(集群):集群是一个或多个节点的集合,它们一起保存整个数据,并提供跨所有节点的联合索引和搜索功能。每个集群都有自己的唯一集群名称,节点通过名称加入集群。 12 | - `Node`(节点):节点是指属于集群的单个 Elasticsearch 实例,存储数据并参与集群的索引和搜索功能。可以将节点配置为按集群名称加入特定集群,默认情况下,每个节点都设置为加入一个名为 elasticsearch 的集群。 13 | - `Index`(索引):索引是一些具有相似特征的文档集合,**类似于 MySQL 中数据库的概念**。 14 | - `Type`(类型):类型是索引的逻辑类别分区,通常,为具有一组公共字段的文档类型,**类似于 MySQL 中表的概念**。注意:由于一些原因,在 Elasticsearch 6.0 以后,一个 Index 只能含有一个 Type。这其中的原因是:相同 index 的不同映射 type 中具有相同名称的字段是相同;在 Elasticsearch 索引中,不同映射 type 中具有相同名称的字段在 Lucene 中被同一个字段支持。在默认的情况下是 `_doc` 。在未来 8.0 的版本中,type 将被彻底删除。 15 | - `Document`(文档):文档是可被索引的基本信息单位,以 JSON 形式表示,**类似于 MySQL 中行的概念**。 16 | - `Field`(字段):**类似于 MySQL 中列的概念**。 17 | - `Shards`(分片):当索引存储大量数据时,可能会超出单个节点的硬件限制,为了解决这个问题,Elasticsearch 提供了将索引细分为分片的概念。分片机制赋予了索引水平扩容的能力,并允许跨分片分发和并行化操作,从而提高性能和吞吐量。 18 | - `Replicas`(副本):在可能出现故障的网络环境中,需要有一个故障切换机制,Elasticsearch 提供了将索引的分片复制为一个或多个副本的功能,副本在某些节点失效的情况下提供高可用性。 19 | 20 | --- 21 | 22 | |Elasticsearch|数据库| 23 | |:-|:-| 24 | |索引 Index|数据库 Database| 25 | |类型 Type|表 Table| 26 | |文档 Document|行 Row| 27 | |字段 Field|列 Column| 28 | 29 | ## 3 安装 30 | 31 | 详情见 [Elasticsearch 安装](https://www.cxy35.top/#/docs/elastic/elasticsearch-install) 32 | 33 | ## 4 常用命令 34 | 35 | 详情见 [Elasticsearch 常用命令](https://www.cxy35.top/#/docs/elastic/elasticsearch-command) 36 | 37 | 38 | --- 39 | 40 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 41 | 42 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/redis/redis-hyperloglog.md: -------------------------------------------------------------------------------- 1 | 本文学习在 Redis 中通过 `HyperLogLog` 实现 UV 统计。 2 | 3 | 4 | ## 1 概述 5 | 6 | 一般我们评估一个网站的访问量,有几个主要的参数:`PV(Page View)`网页的浏览量、`UV(User View)`访问的用户量。有很多第三方工具可以统计,如 cnzz,友盟等。如果自己实现的话,PV 比较简单,可以直接通过 Redis 计数器实现。但是 UV 就不一样,UV 涉及到去重的问题。 7 | 8 | 常规思路:我们首先需要在前端给每一个用户生成一个唯一 id,无论是登录用户还是未登录用户都需要,这个 id 伴随着请求一起到达后端,在后端我们可以通过 `Set` 数据结构中的 `sadd` 命令来存储这个 id,最后通过 `scard` 统计集合大小,进而得出 UV 数据。 9 | 10 | 按上述思路,如果是千万级别的 UV,需要的存储空间就非常惊人,用 Set 就不是很合适了。一般来说像 UV 统计这种,不需要特别精确,比如 800W 和 803W 的 UV,其实差别不大。因此,我们可以使用 `HyperLogLog` 来高效的实现。 11 | 12 | ## 2 基本使用 13 | 14 | Redis 中提供的 HyperLogLog 就是专门用来解决上述问题的,HyperLogLog 提供了一套不怎么精确但是够用的去重方案,会有误差,官方给出的误差数据是 `0.81%`,这对于 UV 的统计够用了。 15 | 16 | HyperLogLog 主要提供了以下命令: 17 | 18 | - `pfadd`:用来添加记录,类似于 sadd ,添加过程中,重复的记录会自动去重。 19 | - `pfcount`:则用来统计数据。 20 | - `pfmerge`:合并多个统计结果,在合并的过程中,会自动去重多个集合中重复的元素。 21 | 22 | 数据量少的时候看不出来误差,我们在 Java 中多添加几个元素: 23 | 24 | ```java 25 | public class HyperLogLogTest { 26 | @Test 27 | public void testHyperLogLog() { 28 | Redis redis = new Redis(); 29 | redis.execute(jedis -> { 30 | for (int i = 0; i < 1000; i++) { 31 | // 重复加入数据,理论值上总数为 1001 32 | jedis.pfadd("uv", "u" + i, "u" + (i + 1)); 33 | } 34 | long uv = jedis.pfcount("uv"); 35 | System.out.println(uv); 36 | }); 37 | } 38 | } 39 | ``` 40 | 41 | 理论值上总数为 1001,实际打印出来 994,有误差,但是在可以接受的范围内。 42 | 43 | --- 44 | 45 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 46 | - 本文示例代码:[https://github.com/cxy35/samples/tree/master/redis/redis-jedis](https://github.com/cxy35/samples/tree/master/redis/redis-jedis) 47 | 48 | 49 | --- 50 | 51 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 52 | 53 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/nginx/nginx-windows-service.md: -------------------------------------------------------------------------------- 1 | 借助第三方工具 WinSW 将 Nginx 添加到 Windows 系统服务,实现自启动。 2 | 3 | 4 | ## 1 准备工作 5 | 6 | WinSW 是一个可执行的二进制文件,可用于将自定义进程包装和管理为 Windows 服务。下载安装包后,可以重命名为任何名称,如: myService.exe 。 7 | 8 | 下载地址:[https://github.com/kohsuke/winsw/releases](https://github.com/kohsuke/winsw/releases),下载 .NET 对应的 exe 文件,如: `WinSW.NET4.exe` 。 9 | 10 | ## 2 配置 11 | 12 | 1. 将 WinSW.NET4.exe 拷贝到 nginx 安装目录下,如:`D:\nginx-1.16.0` ,并重命名为 `nginx-service.exe` 。 13 | 2. 在 nginx 安装目录下新建 `nginx-service.xml` 文件,如下: 14 | 15 | ```xml 16 | 17 | 18 | nginx 19 | 20 | Nginx Service 21 | 22 | High Performance Nginx Service 23 | 24 | D:\nginx-1.16.0\logs-service 25 | 26 | 27 | 10240 28 | 8 29 | 30 | 31 | D:\nginx-1.16.0\nginx.exe 32 | 33 | -p D:\nginx-1.16.0 34 | 35 | D:\nginx-1.16.0\nginx.exe 36 | 37 | -p D:\nginx-1.16.0 -s stop 38 | 39 | ``` 40 | 41 | 3. 在 nginx 安装目录下以管理员身份打开命令行,并执行: 42 | 43 | ```bash 44 | # 安装服务 45 | nginx-service install 46 | # 卸载服务 47 | # nginx-service uninstall 48 | ``` 49 | 50 | 之后在 Windows 系统服务中就能看到 `Nginx Service` 这个服务了,如下: 51 | 52 | ![](https://oscimg.oschina.net/oscnet/up-b5a377097316c54f54f5a459d0031f0fa22.png) 53 | 54 | --- 55 | 56 | - [Nginx 教程合集](https://mp.weixin.qq.com/s/TdLki2vnjW4hKUz_BgzEHg)(微信左下方**阅读全文**可直达)。 57 | 58 | 59 | --- 60 | 61 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 62 | 63 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/docsify-count/docsify-count.js: -------------------------------------------------------------------------------- 1 | //default values 2 | var defaultOptions = { 3 | countable: true, 4 | position: "top", 5 | margin: "10px", 6 | float: "right", 7 | fontsize: "0.9em", 8 | color: "rgb(90,90,90)", 9 | language: "english", 10 | isExpected: true, 11 | } 12 | 13 | // Docsify plugin functions 14 | function plugin(hook, vm) { 15 | if (!defaultOptions.countable) { 16 | return 17 | } 18 | let wordsCount 19 | hook.beforeEach(function (content) { 20 | // Match regex every time you start parsing .md 21 | wordsCount = content.match(/([\u4e00-\u9fa5]+?|[a-zA-Z0-9]+)/g).length 22 | return content 23 | }) 24 | hook.afterEach(function (html, next) { 25 | let str = wordsCount + " words" 26 | let readTime = Math.ceil(wordsCount / 400) + " min" 27 | //Determine whether to use the Chinese style according to the attribute "language" 28 | if (defaultOptions.language === "chinese") { 29 | str = wordsCount + " 字" 30 | readTime = Math.ceil(wordsCount / 400) + " 分钟" 31 | } 32 | 33 | //add html string 34 | next( 35 | ` 36 | ${defaultOptions.position === "bottom" ? html : ""} 37 |
40 | 44 | ${str} 45 | ${defaultOptions.isExpected ? `  |  ${readTime}` : ""} 46 | 47 |
48 |
49 | ${defaultOptions.position !== "bottom" ? html : ""} 50 | ` 51 | ) 52 | }) 53 | } 54 | 55 | // Docsify plugin options 56 | window.$docsify["count"] = Object.assign( 57 | defaultOptions, 58 | window.$docsify["count"] 59 | ) 60 | window.$docsify.plugins = [].concat(plugin, window.$docsify.plugins) 61 | -------------------------------------------------------------------------------- /docs/mysql/mysql-knowledge.md: -------------------------------------------------------------------------------- 1 | 本文记录 MySQL 相关的一些核心知识点。 2 | 3 | 4 | ## 1 存储引擎(MyISAM 和 InnoDB) 5 | 6 | **MyIASM 特点:** 7 | 8 | 1. MyISAM没有提供对数据库事务的支持。 9 | 2. 不支持行级锁和外键。 10 | 3. 由于 2,导致当执行 INSERT 插入或 UPDATE 更新语句时,即执行写操作需要锁定整个表,所以会导致效率降低。 11 | 4. MyISAM 保存了表的行数,当执行 SELECT COUNT(*) FROM TABLE 时,可以直接读取相关值,不用全表扫描,速度快。 12 | 13 | **InnoDB 特点:** 14 | 15 | 1. 支持事务。 16 | 2. 支持 4 个级别的事务隔离。 17 | 3. 支持多版本读。 18 | 4. 支持行级锁。 19 | 5. 读写阻塞与事务隔离级别相关。 20 | 6. 支持缓存,既能缓存索引,也能缓存数据。 21 | 7. 整个表和主键以 Cluster 方式存储,组成一颗平衡树。 22 | 23 | **MyISAM 和 InnoDB 的区别:** 24 | 25 | 1. MyISAM 是非事务安全的,而 InnoDB 是事务安全的。 26 | 2. MyISAM 锁的粒度是表级的,而 InnoDB 支持行级锁。 27 | 3. MyISAM 支持全文类型索引,而 InnoDB 不支持全文索引,一般我们要借助于 Solr 或者 ES 等来做全文索引。 28 | 29 | **使用场景:** 30 | 31 | 1. 如果要执行大量 select 操作,应该选择 MyISAM 。 32 | 2. 如果要执行大量 insert 和 update 操作,应该选择 InnoDB 。 33 | 3. 大尺寸的数据集趋向于选择 InnoDB 引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。主键查询在InnoDB引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题。 34 | 35 | 相对来说,InnoDB 在互联网公司使用更多一些。 36 | 37 | ## 2 事务隔离级别 38 | 39 | 事务的 ACID 特性: 40 | 41 | - 原子性(Atomicity) 42 | - 一致性(Consistency) 43 | - 隔离性(Isolation) 44 | - 持久性(Durability) 45 | 46 | 四个特性中最复杂的,莫过于隔离性了。SQL 标准的事务隔离级别: 47 | 48 | - serializable/串行执行 49 | - repeatable read/可重复读 50 | - read committed/读提交 51 | - read uncommitted/读未提交 52 | 53 | **serializable:** 54 | 55 | 如果隔离级别为 serializable,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离,当然效率也是最低的。 56 | 57 | **repeatable read:** 58 | 59 | 事务不会被看成是一个序列。不过,当前正在执行事务的变化仍然不能被外部看到,也就是说,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。(因为正在执行的事务所产生的数据变化不能被外部看到)。 60 | 61 | **read committed:** 62 | 63 | 安全性比 repeatable read 隔离级别的安全性要差。处于 read committed 级别的事务可以看到其他事务对数据的修改。也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的多个 SELECT 语句可能返回不同的结果。 64 | 65 | **read uncommitted:** 66 | 67 | 提供了事务之间最小限度的隔离。除了容易产生虚幻的读操作和不能重复的读操作外,处于这个隔离级的事务可以读到其他事务还没有提交的数据,如果这个事务使用其他事务不提交的变化作为计算的基础,然后那些未提交的变化被它们的父事务撤销,这就导致了大量的数据变化。不过,这种隔离级别从效率上来说,却是最高的。 68 | 69 | **MySQL 默认的隔离级别则是 repeatable read**,Oracle 默认的隔离级别是 read committed。 70 | 71 | 72 | --- 73 | 74 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 75 | 76 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mq/mq-not-repeatedly-consumed.md: -------------------------------------------------------------------------------- 1 | 如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性? 2 | 3 | 回答这个问题,首先你别听到重复消息这个事儿,就一无所知吧,你先大概说一说可能会有哪些重复消费的问题。 4 | 5 | 首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。 6 | 7 | ## 1 RabbitMQ 的重复消费 8 | 9 | ## 2 Kafka 的重复消费 10 | 11 | Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,**每隔一段时间**(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。 12 | 13 | 但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。 14 | 15 | 举个栗子。 16 | 17 | 有这么个场景。数据 1/2/3 依次进入 kafka,kafka 会给这三条数据每条分配一个 offset,代表这条数据的序号,我们就假设分配的 offset 依次是 152/153/154。消费者从 kafka 去消费的时候,也是按照这个顺序去消费。假如当消费者消费了 offset=153 的这条数据,刚准备去提交 offset 到 zookeeper,此时消费者进程被重启了。那么此时消费过的数据 1/2 的 offset 并没有提交,kafka 也就不知道你已经消费了 offset=153 这条数据。那么重启之后,消费者会找 kafka 说,嘿,哥儿们,你给我接着把上次我消费到的那个地方后面的数据继续给我传递过来。由于之前的 offset 没有提交成功,那么数据 1/2 会再次传过来,如果此时消费者没有去重的话,那么就会导致重复消费。 18 | 19 | ![](https://oscimg.oschina.net/oscnet/up-b573ec719de34c144f00155e11b76cdb860.png) 20 | 21 | ## 3 解决方案 22 | 23 | 如果消费者干的事儿是拿一条数据就往数据库里写一条,会导致说,你可能就把数据 1/2 在数据库里插入了 2 次,那么数据就错啦。 24 | 25 | 其实重复消费不可怕,可怕的是你没考虑到重复消费之后,**怎么保证幂等性**。 26 | 27 | 举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。 28 | 29 | 一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。 30 | 31 | 幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,**不能出错**。 32 | 33 | 所以第二个问题来了,怎么保证消息队列消费的幂等性? 34 | 35 | 其实还是得结合业务来思考,我这里给**几个思路**: 36 | 37 | - 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。 38 | - 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。 39 | - 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。 40 | - 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。 41 | 42 | ![](https://oscimg.oschina.net/oscnet/up-257fe806e997d7d53be0ef8d978556c6781.png) 43 | 44 | 当然,如何保证 MQ 的消费是幂等性的,需要结合具体的业务来看。 45 | 46 | --- 47 | 48 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 49 | 50 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/nginx/nginx-faq.md: -------------------------------------------------------------------------------- 1 | 本文记录 Nginx 使用过程中出现的问题汇总,持续更新中。 2 | 3 | 4 | ## maximum number of descriptors supported by select() is 1024 5 | 6 | 原因: 7 | 8 | 使用 `nginx-1.16.0.zip` 类似这种官方版本的 Nginx ,在 Windows 下因为文件访问句柄数被限制为 1024 了(参数 worker_connections 配置无效),当访问量大时就会无法响应。 9 | 10 | 解决: 11 | 12 | 使用专门的 Windows 版本的 Nginx ,已修改了文件句柄数据的限制。 13 | 14 | - 官网:[http://nginx-win.ecsds.eu/](http://nginx-win.ecsds.eu/) 15 | - 下载地址:[http://nginx-win.ecsds.eu/download/](http://nginx-win.ecsds.eu/download/) 16 | 17 | 下载后 `nginx 1.17.8.1 Unicorn.zip` 里面有个简要的更新信息和安装指南 Readme nginx-win version.txt 。 18 | 19 | 复制 conf 文件夹中的 nginx-win.conf 并重命名为 nginx.conf ,然后在此文件中做配置。用 nginx.exe 启动如果无效,改成用 nginx_basic.exe 启动。 20 | 21 | **如果条件允许的话建议使用 linux 版本。** 22 | 23 | ## ./configure: error: the HTTP rewrite module requires the PCRE library. 24 | 25 | 报错信息如下: 26 | 27 | ``` 28 | ./configure: error: the HTTP rewrite module requires the PCRE library. 29 | You can either disable the module by using --without-http_rewrite_module 30 | option, or install the PCRE library into the system, or build the PCRE library 31 | statically from the source with nginx by using --with-pcre= option. 32 | ``` 33 | 34 | 解决方案:`yum -y install pcre-devel` 35 | 36 | ## ./configure: error: SSL modules require the OpenSSL library. 37 | 38 | 报错信息如下: 39 | 40 | ``` 41 | ./configure: error: SSL modules require the OpenSSL library. 42 | You can either do not enable the modules, or install the OpenSSL library 43 | into the system, or build the OpenSSL library statically from the source 44 | with nginx by using --with-openssl= option. 45 | ``` 46 | 47 | 解决方案:`yum -y install openssl openssl-devel` 48 | 49 | ## 集群中的某个节点正在关闭中或启动中,请求还会被转发过去吗? 50 | 51 | 不会,正在执行的请求会立刻被转发到其他节点上。 52 | 53 | ## 集群中的某个节点被关闭后,请求还会被转发过去吗? 54 | 55 | 不会。但有些时候请求要等 1 分钟才能收到其他节点的返回结果,偶尔出现,为什么? 56 | 57 | ## 集群中的某个节点关闭再启动,请求还会被转发过去吗? 58 | 59 | 居然不会,要重启 Nginx 才会生效,为什么? 60 | 61 | --- 62 | 63 | - [Nginx 教程合集](https://mp.weixin.qq.com/s/TdLki2vnjW4hKUz_BgzEHg)(微信左下方**阅读全文**可直达)。 64 | 65 | 66 | --- 67 | 68 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 69 | 70 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/sublime-keymap.md: -------------------------------------------------------------------------------- 1 | 工欲善其事,必先利其器。本文收集 Sublime Text 常用快捷键,持续更新中...... 2 | 3 | 4 | ## 通用(General) 5 | 6 | - `↑↓←→`:上下左右移动光标 7 | - `Alt`:调出菜单 8 | - `Ctrl + Shift + P`:调出命令板(Command Palette) 9 | - Ctrl + `:调出控制台 10 | 11 | ## 编辑(Editing) 12 | 13 | - `Ctrl + Enter`:在当前行下面新增一行然后跳至该行 14 | - `Ctrl + Shift + Enter`:在当前行上面增加一行并跳至该行 15 | - `Ctrl + ←/→`:进行逐词移动 16 | - `Ctrl + Shift + ←/→`:进行逐词选择 17 | - `Ctrl + ↑/↓`:移动当前显示区域 18 | - `Ctrl + Shift + ↑/↓`:移动当前行 19 | 20 | ## 选择(Selecting) 21 | 22 | - `Shift + 右键`:列编辑,需同时按住 23 | - `Ctrl + Shift + L`:选中需要进行列编辑的多行,然后按下 `Ctrl + Shift + L` 也可以开启列编辑模式,适合行末列编辑 24 | - `Ctrl + D`:选择当前光标所在的词并高亮该词所有出现的位置,再次 `Ctrl + D` 选择该词出现的下一个位置,在多重选词的过程中,使用 `Ctrl + K` 进行跳过,使用 `Ctrl + U` 进行回退,使用 `Esc` 退出多重编辑 25 | - `Ctrl + J`:把当前选中区域合并为一行 26 | - `Ctrl + M`:在起始括号和结尾括号间切换 27 | - `Ctrl + Shift + M`:快速选择括号间的内容 28 | - `Ctrl + Shift + J`:快速选择同缩进的内容 29 | - `Ctrl + Shift + Space`:快速选择当前作用域(Scope)的内容 30 | 31 | ## 查找&替换(Finding&Replacing) 32 | 33 | - `F3`:跳至当前关键字下一个位置 34 | - `Shift + F3`:跳到当前关键字上一个位置 35 | - `Alt + F3`:选中当前关键字出现的所有位置 36 | - `Ctrl + F/H`:进行标准查找/替换,之后: 37 | - `Alt + C`:切换大小写敏感(Case-sensitive)模式 38 | - `Alt + W`:切换整字匹配(Whole matching)模式 39 | - `Alt + R`:切换正则匹配(Regex matching)模式 40 | - `Ctrl + Shift + H`:替换当前关键字 41 | - `Ctrl + Alt + Enter`:替换所有关键字匹配 42 | - `Ctrl + Shift + F`:多文件搜索&替换 43 | 44 | ## 跳转(Jumping) 45 | 46 | - `Ctrl + P`:跳转到指定文件,输入文件名后可以: 47 | - `@` 符号跳转:输入 `@symbol` 跳转到 `symbol` 符号所在的位置 48 | - `#` 关键字跳转:输入 `#keyword` 跳转到 `keyword` 所在的位置 49 | - `:` 行号跳转:输入 `:12` 跳转到文件的第12行。 50 | - `Ctrl + R`:跳转到指定符号 51 | - `Ctrl + G`:跳转到指定行号 52 | 53 | ## 窗口(Window) 54 | 55 | - `Ctrl + Shift + N`:创建一个新窗口 56 | - `Ctrl + N`:在当前窗口创建一个新标签 57 | - `Ctrl + W`:关闭当前标签,当窗口内没有标签时会关闭该窗口 58 | - `Ctrl + Shift + T`:恢复刚刚关闭的标签 59 | 60 | ## 屏幕(Screen) 61 | 62 | - `F11`:切换普通全屏 63 | - `Shift + F11`:切换无干扰全屏 64 | - `Alt + Shift + 2`:进行左右分屏 65 | - `Alt + Shift + 8`:进行上下分屏 66 | - `Alt + Shift + 5`:进行上下左右分屏 67 | - 分屏之后,使用 `Ctrl + 数字键` 跳转到指定屏,使用 `Ctrl + Shift + 数字键` 将当前屏移动到指定屏 68 | 69 | 70 | --- 71 | 72 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 73 | 74 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-procedure.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 MySQL 存储过程的语法和使用。 2 | 3 | 4 | ## 1 基本语法 5 | 6 | ```sql 7 | CREATE OR REPLACE PROCEDURE 存储过程名 ( 8 |     -- in/out 参数名 参数类型 9 |     in param1 varchar(32), 10 |     out param2 varchar(32) 11 | ) 12 | BEGIN 13 |     -- 变量名 变量类型(取值范围) 14 |     declare var1 varchar(32); 15 |     declare var2 varchar(32) default ''; 16 |      17 |     -- 定义游标遍历时,作为判断是否遍历完全部记录的标记 18 |     declare _done int default 0; 19 |     -- 定义游标 20 |     declare _pqList cursor for select o.`uid`,o.`name` from grid_org_org o where o.type='17'; 21 |     -- 定义处理程序,声明当游标遍历完全部记录后将标志变量置成某个值 22 |     declare continue handler for not found set _done=1; 23 |      24 |     -- 具体实现 25 | END; 26 | ``` 27 | 28 | ## 2 给变量赋值 29 | 30 | ```sql 31 | -- 方法1 32 | set var1='100'; 33 | -- 方法2 34 | select col1,col2 into var1,var2 from test_tb; 35 | -- 方法3 36 | set @sqlStr='select count(*) from test_tb';  -- 构造 sql 赋值给一个变量(可以之前没有定义,但要以@开头) 37 | prepare var1 from @sqlStr; -- 预处理需要执行的动态 sql ,其中 var1 是一个变量 38 | execute var1;  -- 执行sql语句 39 | deallocate prepare var1;     -- 释放掉预处理段 40 | ``` 41 | 42 | ## 3 游标 43 | 44 | ### 3.1 cursor 型游标(**不能用于参数传递**)  45 | 46 | - 赋值 47 | 48 | ```sql 49 | -- 定义变量时赋值 50 | declare var3 cursor for select col1 from temp_tb where id='1'; 51 | ``` 52 | 53 | - 遍历 54 | 55 | ```sql 56 | open _pqList; 57 | loop_label:loop 58 |     fetch _pqList into var1,var2; 59 |     if _done=1 then 60 |         leave loop_label; 61 |     end if; 62 | 63 |     -- 具体操作 64 |     if var1 is not null and length(var1)>0 then 65 | 66 |     end if; 67 | end loop; 68 | close _pqList; 69 | ``` 70 | 71 | ## 4 实例 72 | 73 | ```sql 74 | create or replace procedure p_test ( 75 |   in var1 varchar(32),-- 输入参数 76 |   out var2 varchar(32)-- 输出普通参数 77 | ) 78 | begin 79 |   -- 输出普通参数 80 |   select col1 into var2 from test_tb where id=var1; 81 |   -- set var2='10'; 82 |    83 |   -- 输出结果集,可多个,不用作为参数传进来 84 |   select * from test_tb where id='1'; 85 |   select * from test_tb where id='2'; 86 | end; 87 | ``` 88 | 89 | --- 90 | 91 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 92 | 93 | 94 | --- 95 | 96 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 97 | 98 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/idea-keymap.md: -------------------------------------------------------------------------------- 1 | 工欲善其事,必先利其器。本文收集 IntelliJ IDEA 常用快捷键,持续更新中...... 2 | 3 | 4 | - **搜索所有文件:双击Shift** 5 | - **搜索类文件:Ctrl+N** 6 | - **搜索文件:Ctrl+Shift+N** 7 | - 搜索函数:Ctrl+Alt+Shift+N 8 | - **全局内容搜索:Ctrl+Shift+F** 9 | - **全局内容替换:Ctrl+Shift+R** 10 | 11 | --- 12 | 13 | - 快速打开或隐藏工程面板:Alt+1 14 | - 切换代码视图:Alt+左箭头/右箭头 15 | - 关闭当前代码文件:Ctrl+F4 16 | 17 | --- 18 | 19 | - 查看近期文件:Ctrl+E 20 | - 查看近期位置:Ctrl+Shift+E 21 | - **查看变量、方法-参数/返回值、接口:Ctrl+F12** 22 | - 查看类结构图/继承层次:Ctrl+H 23 | - **查看注释文档(光标处的类或方法):Ctrl+Q** 24 | - 查看Maven依赖图、类图:Ctrl+Shift+Alt+U 25 | - **查看某一个方法或者变量在哪里被使用了:ALT+F7** 26 | - 高亮错误或警告快速定位:F2 或 Shift+F2 27 | 28 | --- 29 | 30 | - 跳转到上一次修改的地方:Ctrl+Shift+Backspace 31 | - 跳转到上一个浏览的地方:Ctrl+Alt+左箭头 32 | - 跳转到下一个浏览的地方:Ctrl+Alt+右箭头 33 | - **跳转到类或方法的实现处:Ctrl+Alt+B 或 Ctrl+Alt+鼠标左键** 34 | - 跳转到类或方法的定义处:Ctrl+B/Ctrl+鼠标左键 35 | - **代码提示:Ctrl+ALT+空格键** 36 | - **代码注释(单行):Ctrl+/** 37 | - **代码注释(多行):Ctrl+Shift+/** 38 | - **变量或者类重命名:Shift+F6** 39 | - 重构函数:Ctrl+F6 40 | - **生成 Constructor/Getter/Setter/equals/hashCode/toString 等方法:Alt+Insert** 41 | - **选择可以覆盖的方法:Ctrl+O** 42 | - **选择可以实现的方法:Ctrl+I** 43 | - 列编辑:按住 Alt,多行时再按住 Shift 44 | - 代码包裹(for/if/trycache等):Ctrl+ALT+T 45 | - **代码格式化:Ctrl+Alt+L** 46 | - **移除无用的import:Ctrl+Alt+O** 47 | - **大小写切换:Ctrl+Shift+U** 48 | - 编译:Ctrl+F9 49 | 50 | --- 51 | 52 | - **代码复制到新的一行:Ctrl+D** 53 | - **代码向上或者向下移动一行:Ctrl+Shift+↑/↓** 54 | - **删除当前行:Ctrl+Y** 55 | - **在当前行的上面创建新的一行:Ctrl+Alt+Enter** 56 | - **在当前行的下面创建新的一行:Ctrl+Shift+Enter** 57 | - 打开复制内容列表供选择粘贴:Ctrl+Shift+V 58 | - 抽取变量:Ctrl+Alt+V 59 | - 抽取静态变量:Ctrl+Alt+C 60 | - 抽取成员变量:Ctrl+Alt+F 61 | - 抽取方法:Ctrl+Alt+M 62 | - 抽取方法参数:Ctrl+Alt+P 63 | - **在当前目录下新建类/包/文件等:Ctrl+Alt+Insert** 64 | 65 | --- 66 | 67 | Alt+Enter: 68 | - 提供代码提示。 69 | - 自动创建函数。 70 | - list replace。 71 | - 实现接口。 72 | - 单词拼写。 73 | - 导包。 74 | 75 | --- 76 | 77 | 配置 Settings: 78 | - 模板 Live Templates:如输入 `psvm` 会自动生成 `public static void main` 方法。`Ctrl+J` 可查看并选择。 79 | - 后缀补全 Postfix Completion:如输入 `o.null` 会自动生成 `if(o == null){}` 代码块;输入 `b.sout` 会自动生成 `System.out.println(b);` 代码块。 80 | - 插件 Plugins:如编码规范插件(Alibaba Java Coding Guidelines)。 81 | 82 | 83 | --- 84 | 85 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 86 | 87 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/linux/linux-netstat-ps.md: -------------------------------------------------------------------------------- 1 | Linux 下查看某个端口被哪个进程或程序占用。 2 | 3 | 4 | ```bash 5 | [root@dbserver ~]$ netstat -anp|grep 3306 6 | tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 3005/mysqld 7 | tcp 0 0 192.168.71.62:3306 192.168.71.98:57629 ESTABLISHED 3005/mysqld 8 | tcp 0 0 192.168.71.62:3306 192.168.11.118:57895 ESTABLISHED 3005/mysqld 9 | tcp 0 0 192.168.71.62:3306 192.168.71.98:57639 ESTABLISHED 3005/mysqld 10 | tcp 0 0 192.168.71.62:3306 192.168.71.97:57139 ESTABLISHED 3005/mysqld 11 | tcp 0 0 192.168.71.62:3306 192.168.71.56:57896 ESTABLISHED 3005/mysqld 12 | tcp 0 0 192.168.71.62:3306 192.168.71.35:58646 ESTABLISHED 3005/mysqld 13 | tcp 0 0 192.168.71.62:3306 192.168.71.35:58629 ESTABLISHED 3005/mysqld 14 | tcp 0 0 192.168.71.62:3306 192.168.71.97:57132 ESTABLISHED 3005/mysqld 15 | unix 2 [ ACC ] STREAM LISTENING 13125 3005/mysqld /usr/local/mysql3306/data/mysqld.sock 16 | ``` 17 | 18 | 由上面可以看到端口 3306 对应的进程 pid=3005 ,进程为 mysqld 。 19 | 20 | 再执行 **kill -9 3005** 可结束该进程。 21 | 22 | ```bash 23 | [root@dbserver ~]$ ps -ef|grep mysqld 24 | root 2123 1 0 09:39 ? 00:00:00 /bin/sh /usr/local/mysql3306/bin/mysqld_safe --datadir=/usr/local/mysql3306/data --pid-file=/usr/local/mysql3306/data/dbserver.pid 25 | mysql 3005 2123 1 09:39 ? 00:03:40 /usr/local/mysql3306/bin/mysqld --basedir=/usr/local/mysql3306 --datadir=/usr/local/mysql3306/data --plugin-dir=/usr/local/mysql3306/lib/plugin --user=mysql --log-error=/usr/local/mysql3306/data/error.log --open-files-limit=8192 --pid-file=/usr/local/mysql3306/data/dbserver.pid --socket=/usr/local/mysql3306/data/mysqld.sock --port=3306 26 | root 4186 3632 0 14:23 pts/0 00:00:00 grep mysqld 27 | ``` 28 | 29 | 由上面可以看到进程 pid=3005 对应的 mysqld 进程的具体信息。 30 | 31 | 32 | --- 33 | 34 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 35 | 36 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/prismjs/components/prism-java.js: -------------------------------------------------------------------------------- 1 | (function (Prism) { 2 | 3 | var keywords = /\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|null|open|opens|package|private|protected|provides|public|requires|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/; 4 | 5 | // based on the java naming conventions 6 | var className = /\b[A-Z](?:\w*[a-z]\w*)?\b/; 7 | 8 | Prism.languages.java = Prism.languages.extend('clike', { 9 | 'class-name': [ 10 | className, 11 | 12 | // variables and parameters 13 | // this to support class names (or generic parameters) which do not contain a lower case letter (also works for methods) 14 | /\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/ 15 | ], 16 | 'keyword': keywords, 17 | 'function': [ 18 | Prism.languages.clike.function, 19 | { 20 | pattern: /(\:\:)[a-z_]\w*/, 21 | lookbehind: true 22 | } 23 | ], 24 | 'number': /\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i, 25 | 'operator': { 26 | pattern: /(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m, 27 | lookbehind: true 28 | } 29 | }); 30 | 31 | Prism.languages.insertBefore('java', 'string', { 32 | 'triple-quoted-string': { 33 | // http://openjdk.java.net/jeps/355#Description 34 | pattern: /"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/, 35 | greedy: true, 36 | alias: 'string' 37 | } 38 | }); 39 | 40 | Prism.languages.insertBefore('java', 'class-name', { 41 | 'annotation': { 42 | alias: 'punctuation', 43 | pattern: /(^|[^.])@\w+/, 44 | lookbehind: true 45 | }, 46 | 'namespace': { 47 | pattern: /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(?:\.[a-z]\w*)+/, 48 | lookbehind: true, 49 | inside: { 50 | 'punctuation': /\./, 51 | } 52 | }, 53 | 'generics': { 54 | pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, 55 | inside: { 56 | 'class-name': className, 57 | 'keyword': keywords, 58 | 'punctuation': /[<>(),.:]/, 59 | 'operator': /[?&|]/ 60 | } 61 | } 62 | }); 63 | }(Prism)); 64 | -------------------------------------------------------------------------------- /docs/tool/idea-plugins-easycode.md: -------------------------------------------------------------------------------- 1 | IntelliJ IDEA 中的插件 EasyCode,用于**代码自动生成**,支持模板自定义、导入、导出,方便团队之间共享。 2 | 3 | 4 | ## 1 介绍 5 | 6 | - 基于 IntelliJ IDEA 开发的代码生成插件,支持自定义任意模板(Java,html,js,xml)。 7 | - 只要是与数据库相关的代码都可以通过自定义模板来生成。支持数据库类型与 java 类型映射关系配置。 8 | - 支持同时生成多张表的代码。每张表有独立的配置信息。完全的个性化定义,规则由你设置。 9 | 10 | ## 2 安装 11 | 12 | `File -> Settings -> Plugins`,在插件市场中搜索 `EasyCode` 安装,重启 IDEA。 13 | 14 | ## 3 使用 15 | 16 | ### 3.1 创建项目 17 | 18 | `File -> New Project -> Spring Initializr` 。 19 | 20 | ### 3.2 添加数据源 21 | 22 | EasyCode 是基于 IDEA 上的 Database Tools 开发的,因此要通过 IDEA 上的 `Database` 连接数据源。 23 | 24 | ![](https://oscimg.oschina.net/oscnet/up-74e0b0d3c2e6245e9e29f4d21661cfdc150.JPEG) 25 | 26 | 配置数据库连接信息。 27 | 28 | ![](https://oscimg.oschina.net/oscnet/up-a099792cfa639dddcd549ffef7702274818.JPEG) 29 | 30 | ### 3.3 生成代码 31 | 32 | 选择对应的数据库和表(支持多张表同时生成),右键单击,选择 `EasyCode -> Generate Code` 。 33 | 34 | ![](https://oscimg.oschina.net/oscnet/up-f4ac0513e30aadd503dcaf89507ac53d008.JPEG) 35 | 36 | 可能需要添加部分数据库类型与 Java 类型的映射关系。 37 | 38 | ![](https://oscimg.oschina.net/oscnet/up-2eb4107d45073537d99d20caff974e0add7.JPEG) 39 | 40 | ![](https://oscimg.oschina.net/oscnet/up-76f65701be38eebb9d714e2c4e12df41c8d.JPEG) 41 | 42 | 支持单张表单独配置,右键单击,选择 `EasyCode -> Config Table` 。 43 | 44 | ![](https://oscimg.oschina.net/oscnet/up-0241e081fb46cfd6894840d7acc1310cf98.JPEG) 45 | 46 | ![](https://oscimg.oschina.net/oscnet/up-c8c5f8e3b520aa77ae108af2960cd98ab3c.JPEG) 47 | 48 | 配置生成代码。 49 | 50 | ![](https://oscimg.oschina.net/oscnet/up-a5d7686239f279e43de922fac4b39372c8d.JPEG) 51 | 52 | 如果 `controller/entity/service/dao` 等包不存在会提示自动创建。最终生成的代码如下: 53 | 54 | ![](https://oscimg.oschina.net/oscnet/up-1f49b8c77616760e708e796ffa9703beb20.JPEG) 55 | 56 | ### 3.4 自定义模板 57 | 58 | 支持**自定义模板**,并且可以**实时调试**。 59 | 60 | `File -> Settings -> Easy Code -> Template Setting` 。 61 | 62 | ![](https://oscimg.oschina.net/oscnet/up-e5bc71bd3cd7464e0e0ecd470388c5270f7.JPEG) 63 | 64 | **建议自己新建一套模板(包括 `Type Mapper/Template Setting/Global Config` 等),默认的 Default 模板供参考**。 65 | 66 | ### 3.5 模板共享 67 | 68 | 支持模板导入、导出,方便团队之间共享。 69 | 70 | `File -> Settings -> Easy Code` 。 71 | 72 | ![](https://oscimg.oschina.net/oscnet/up-71913d7e081d75241a66b147f77909b8cb5.png) 73 | 74 | 75 | --- 76 | 77 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 78 | 79 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/redis/redis-command.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Redis 常用命令。 2 | 3 | 4 | ## 1 基本数据类型相关命令 5 | 6 | 参考 [Redis 基本数据类型(字符串、列表、集合、散列、有序集合)](https://mp.weixin.qq.com/s/BdHrMOU_9UwUNYDmMMrD6w) 。 7 | 8 | ## 2 key 相关命令 9 | 10 | key 相关的命令,对不同的数据类型都通用。 11 | 12 | - del 13 | 14 | 以删除一个已经存在的 key 。 15 | 16 | ```bash 17 | 127.0.0.1:6379> set k1 hello 18 | OK 19 | 127.0.0.1:6379> del k1 20 | (integer) 1 21 | 127.0.0.1:6379> get k1 22 | (nil) 23 | ``` 24 | 25 | - exists 26 | 27 | 检测一个给定的 key 是否存在。 28 | 29 | ```bash 30 | 127.0.0.1:6379> set k1 hello 31 | OK 32 | 127.0.0.1:6379> exists k1 33 | (integer) 1 34 | 127.0.0.1:6379> exists k2 35 | (integer) 0 36 | ``` 37 | 38 | - keys 39 | 40 | 获取满足给定模式的所有 key 。 `keys *` 表示获取所有的 key , `*` 也可以是一个正则表达式。 41 | 42 | ```bash 43 | 127.0.0.1:6379> mset k1 hello k2 redis name cxy35 44 | OK 45 | 127.0.0.1:6379> keys * 46 | 1) "name" 47 | 2) "k2" 48 | 3) "k1" 49 | 127.0.0.1:6379> keys k* 50 | 1) "k2" 51 | 2) "k1" 52 | ``` 53 | 54 | - ttl/pttl 55 | 56 | 查看 key 的有效期(秒/毫秒)。 -2 表示 key 不存在或者已过期, -1 表示 key 存在并且没有设置过期时间(永久有效)。默认为 -1 。 57 | 58 | ```bash 59 | 127.0.0.1:6379> set k1 hello 60 | OK 61 | 127.0.0.1:6379> ttl k1 62 | (integer) -1 63 | 127.0.0.1:6379> ttl k2 64 | (integer) -2 65 | ``` 66 | 67 | - expire/pexpire 68 | 69 | 给 key 设置有效期(秒/毫秒),在有效期过后,key 会被销毁。 70 | 71 | ```bash 72 | 127.0.0.1:6379> set k1 hello 73 | OK 74 | 127.0.0.1:6379> ttl k1 75 | (integer) -1 76 | 127.0.0.1:6379> expire k1 10 77 | (integer) 1 78 | 127.0.0.1:6379> ttl k1 79 | (integer) 6 80 | ``` 81 | 82 | - persist 83 | 84 | 移除一个 key 的过期时间,这样该 key 就永远不会过期。 85 | 86 | ```bash 87 | 127.0.0.1:6379> set k1 hello 88 | OK 89 | 127.0.0.1:6379> ttl k1 90 | (integer) -1 91 | 127.0.0.1:6379> expire k1 30 92 | (integer) 1 93 | 127.0.0.1:6379> ttl k1 94 | (integer) 27 95 | 127.0.0.1:6379> persist k1 96 | (integer) 1 97 | 127.0.0.1:6379> ttl k1 98 | (integer) -1 99 | ``` 100 | 101 | - dump 102 | 103 | 序列化给定的 key,并返回序列化之后的值。 104 | 105 | ```bash 106 | 127.0.0.1:6379> set k1 hello 107 | OK 108 | 127.0.0.1:6379> dump k1 109 | "\x00\x05hello\t\x00\xb3\x80\x8e\xba1\xb2C\xbb" 110 | ``` 111 | 112 | --- 113 | 114 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 115 | 116 | 117 | --- 118 | 119 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 120 | 121 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-user-privileges.md: -------------------------------------------------------------------------------- 1 | 学习 MySQL 的用户管理与权限管理。 2 | 3 | 4 | ## MySQL 用户 5 | 6 | ```sql 7 | -- 创建用户 'test'@'%' 8 | CREATE USER test IDENTIFIED BY '123456'; 9 | CREATE USER 'test'@'%' IDENTIFIED BY '123456'; 10 | -- 创建用户 'test'@'localhost' 11 | CREATE USER 'test'@'localhost' IDENTIFIED BY '123456'; 12 | flush privileges; 13 | 14 | -- 删除用户 'test'@'%' 15 | drop user 'test'@'%'; 16 | -- 删除用户 'test'@'localhost' 17 | drop user 'test'@'localhost'; 18 | flush privileges; 19 | ``` 20 | 21 | ## MySQL 权限 22 | 23 | ```sql 24 | -- 权限汇总 25 | -- SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER 26 | 27 | -- 授权/取消,单个权限 28 | GRANT SELECT ON mydbname.mytbname TO 'test'@'%'; 29 | REVOKE SELECT ON mydbname.mytbname FROM 'test'@'%'; 30 | GRANT SELECT ON mydbname.* TO 'test'@'%'; 31 | REVOKE SELECT ON mydbname.* FROM 'test'@'%'; 32 | flush privileges; 33 | 34 | -- 授权/取消,多个权限 35 | GRANT SELECT, INSERT ON mydbname.mytbname TO 'test'@'%'; 36 | REVOKE SELECT, INSERT ON mydbname.mytbname FROM 'test'@'%'; 37 | flush privileges; 38 | 39 | -- 授权/取消,所有权限 40 | GRANT ALL PRIVILEGES ON mydbname.mytbname TO 'test'@'%'; 41 | REVOKE ALL PRIVILEGES ON mydbname.mytbname FROM 'test'@'%'; 42 | flush privileges; 43 | 44 | -- 查看已授权的权限 45 | show grants for 'test'@'%'; 46 | show grants for 'test'@'localhost'; 47 | ``` 48 | 49 | |权限|说明| 50 | |:-|:-| 51 | |all| | 52 | |alter| | 53 | |alter routine|使用alter procedure和drop procedure| 54 | |create| | 55 | |create routine|使用create  procedure| 56 | |create temporary tables|使用create temporary table| 57 | |create user| | 58 | |create view| | 59 | |delete| | 60 | |drop| | 61 | |execute|使用call和存储过程| 62 | |file|使用select into outfile和load data infile| 63 | |grant option|使用grant和revoke| 64 | |index|使用create index和drop index| 65 | |insert| | 66 | |lock tables|锁表| 67 | |process|使用show full processlist| 68 | |reload|使用flush| 69 | |replication client|服务器位置访问| 70 | |replication slave|由复制从属使用| 71 | |select| | 72 | |show databases| | 73 | |show view| | 74 | |shutdown|使用mysqladmin shutdown来关闭mysql| 75 | |super| | 76 | |update| | 77 | |usage|无访问权限| 78 | 79 | --- 80 | 81 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 82 | 83 | 84 | --- 85 | 86 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 87 | 88 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/linux/linux-command-tar.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Linux 常用命令-打包、压缩、解压缩:tar 。 2 | 3 | 4 | ## 名词区分 5 | 6 | - 打包:将一大堆文件或目录变成一个总的文件( tar 命令) 7 | - 压缩:将一个大的文件通过一些压缩算法变成一个小文件( gzip,bzip2 等) 8 | 9 | Linux 中很多压缩程序只能针对一个文件进行压缩,因此当你想要压缩一大堆文件时,你得将这一大堆文件先打成一个包( tar 命令),然后再用压缩程序进行压缩( gzip,bzip2 命令)。 10 | 11 | 习惯上以 .tar 后缀代表 tar 包,用 xxx.tar.gz 或 .tgz 代表 gzip 压缩过的 tar 文件,用 .tar.bz2 代表 bzip2 压缩过的 tar 文件。 12 | 13 | ## 语法 14 | 15 | - tar [主选项 + 辅选项] 文件或目录 16 | 17 | 使用该命令时,主选项必须有,且仅有一个,如:tar -xzvf mysql-5.6.42-linux2.6-x86_64.tar.gz 18 | 19 | ## 主选项 20 | 21 | 使用该命令时,主选项必须有,且仅有一个。 22 | 23 | - -c: 新建一个压缩文档,即打包 24 | - -x: 解压文件 25 | - -t: 查看压缩文档里的所有内容 26 | - -r: 向压缩文档里追加文件 27 | - -u: 更新原压缩包中的文件 28 | 29 | ## 辅助选项 30 | 31 | - -z:具有 gzip 属性,一般格式为 xxx.tar.gz 或xx.tgz 32 | - -j:具有 bzip2 属性,一般格式为 xx.tar.bz2 33 | - -Z:具有 compress 属性,一般格式为 xx.tar.Z 34 | - -v:显示操作过程 35 | - -f:使用文档名,在f之后要立即接文档名,不要再加其他参数 36 | - -C:打包/压缩时可将当前目录更改为指定的目录,详见下文 37 | 38 | ## 打包/压缩 39 | 40 | - tar -cvf img.tar img1 img2 --> 注:将当前目录下 img1 和 img2 两个文件夹打包成 img.tar ,仅打包不压缩 41 | - tar -czvf img.tar.gz img1 img2 --> 注:将当前目录下 img1 和 img2 两个文件夹打包成 img.tar.gz ,打包后,以 gzip 压缩 42 | - tar -cjvf img.tar.bz2 img1 img2 --> 注:将当前目录下 img1 和 img2 两个文件夹打包成 img.tar.bz2 ,打包后,以 bzip2 来压缩 43 | - tar -cvf img.tar -C /usr/local aaa --> 注:将当前目录改为 /usr/local ,并将 /usr/local 下的aaa目录打包到 img.tar 44 | 45 | ## 不解压的情况下查看 46 | 47 | - tar -tvf img.tar --> 注:查看当前目录下 img.tar 中的所有内容 48 | 49 | ## 解压 50 | 51 | - tar -xvf img.tar --> 注:将 img.tar 解压到当前目录 52 | - tar -xvf img.tar img1 --> 注:将 img.tar 解压到当前目录,但只减压 img.tar 中的 img1 文件夹 53 | - tar -xvf img.tar -C /usr/local --> 注:将当前目录改为 /usr/local ,并将 img.tar 解压到 /usr/local 目录 54 | 55 | ## 更新 56 | 57 | - tar -uvf img.tar img1 --> 注:将 img1 文件夹更新到 img.tar 中 58 | 59 | ## 追加 60 | 61 | - tar -rvf img.tar img3 --> 注:将 img3 文件夹追加到 img.tar 中 62 | 63 | ## C 参数 64 | 65 | -C dir 参数的作用在于改变工作目录,其有效期为该命令中下一次 -C dir 参数之前。 66 | 67 | - tar -cvf img.tar -C /usr/local aaa --> 注:将当前目录改为 /usr/local ,并将 /usr/local 下的 aaa 目录打包到 img.tar 68 | - tar -xvf img.tar -C /usr/local --> 注:将当前目录改为 /usr/local ,并将 img.tar 解压到 /usr/local 目录 69 | 70 | ## 解压方法总结 71 | 72 | - *.tar 用 tar –xvf 解压 73 | - *.gz 用 gzip -d或者 gunzip 解压 74 | - *.tar.gz和*.tgz 用 tar –xzf 解压 75 | - *.bz2 用 bzip2 -d 或者用 bunzip2 解压 76 | - *.tar.bz2 用 tar –xjf 解压 77 | - *.Z 用 uncompress 解压 78 | - *.tar.Z 用 tar –xZf 解压 79 | - *.rar 用 unrar x 解压,需先安装 80 | - *.zip 用 unzip 解压,需先安装 81 | 82 | 83 | --- 84 | 85 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 86 | 87 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-install-windows.md: -------------------------------------------------------------------------------- 1 | 手把手带你在 Windows 上安装 MySQL-5.6 。 2 | 3 | 4 | ```bash 5 | # 下载免安装版本的 MySQL 6 | # https://downloads.mysql.com/archives/community/ 7 | # mysql-5.6.42-winx64.zip 8 | 9 | # 解压并准备配置文件 my.ini 放到下列目录中 10 | D:\mysql-5.6.42-3306 11 | 12 | # 修改配置文件 my.ini 13 | [client] 14 | port = 3306 15 | #socket = D:\mysqldata\tmp\mysql.sock 16 | default-character-set = utf8 17 | 18 | [mysql] 19 | default-character-set = utf8 20 | prompt = "\\u:\\d> " 21 | auto-rehash 22 | 23 | [mysqld] 24 | port = 3306 25 | basedir = D:\mysql-5.6.42-3306 26 | datadir = D:\mysql-5.6.42-3306\data 27 | #socket = D:\mysql-5.6.42-3306\tmp\mysql.sock 28 | #tmpdir = D:\mysql-5.6.42-3306\tmp 29 | #log_bin = D:\mysql-5.6.42-3306\log\mysql-bin.log 30 | #relay_log = D:\mysql-5.6.42-3306\log\mysql-relay-bin.log 31 | #log_error = D:\mysql-5.6.42-3306\log\alert.log 32 | #slow_query_log_file = D:\mysql-5.6.42-3306\log\mysql_slow.log 33 | default-time-zone = '+8:00' 34 | character-set-server = utf8 35 | collation-server = utf8_unicode_ci 36 | init_connect = 'SET NAMES utf8' 37 | max_connections = 1000 38 | max_user_connections = 1000 39 | max_connect_errors = 90000000 40 | max_allowed_packet = 16M 41 | back_log = 5000 42 | wait_timeout = 120 43 | interactive_timeout = 120 44 | sort_buffer_size = 2M 45 | join_buffer_size = 2M 46 | server-id = 1 47 | default-storage-engine = innodb 48 | innodb-file-per-table = 1 49 | ...... 50 | 51 | # 初始化 52 | d: 53 | cd mysql-5.6.42-3306/bin 54 | mysqld.exe --initialize 55 | 56 | # 将 mysql 从 windows 服务中移除 57 | mysqld.exe --remove MySQL-3306 58 | 59 | # 将 mysql 添加到 windows 服务中 60 | mysqld.exe --install MySQL-3306 --defaults-file="D:\mysql-5.6.42-3306\my.ini" 61 | 62 | # 启动服务 63 | net start MySQL-3306 64 | 65 | # 登录并修改密码(有些时候需要初始密码) 66 | cd d:/mysql-5.6.42-3306/bin 67 | mysql -uroot 68 | # mysql -uroot -p 69 | update mysql.user set password = password('123456') where user='root'; 70 | # alter user 'root'@'localhost' identified by '123456'; 71 | flush privileges; 72 | 73 | # 授权远程访问 74 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION; 75 | flush privileges; 76 | 77 | # 关闭服务 78 | net stop MySQL-3306 79 | 80 | # 根据端口查询 pid 81 | netstat -ano|findstr "3306" 82 | 83 | # 根据 pid 查询进程名 84 | tasklist|findstr "9452" 85 | 86 | # 杀进程 87 | taskkill /f /pid 9452 88 | ``` 89 | 90 | --- 91 | 92 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 93 | 94 | 95 | --- 96 | 97 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 98 | 99 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/maven-command.md: -------------------------------------------------------------------------------- 1 | Maven 常用命令。 2 | 3 | 4 | - 创建 Maven 的普通 java 项目:mvn archetype:generate -DgroupId=com.demo -DartifactId=demo_maven -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 5 | - 创建 Maven 的 Web 项目:mvn archetype:generate -DgroupId=com.demo -DartifactId=demo_maven2 -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false 6 | - 编译源代码: mvn compile 7 | - 编译测试代码:mvn test-compile 8 | - 运行测试:mvn test 9 | - 产生 site:mvn site 10 | - 打包:mvn package 11 | - 在本地 Repository 中安装 jar:mvn install 12 | - 清除产生的项目:mvn clean 13 | - 跳过测试运行:mvn package -DskipTests 14 | - 跳过测试编译和运行:mvn package -Dmaven.test.skip=true 15 | - 只打 jar 包: mvn jar:jar 16 | - 生成 idea 项目:mvn idea:idea 17 | - 生成 eclipse 项目:mvn eclipse:eclipse 18 | - 清除 eclipse 的一些系统设置:mvn eclipse:clean 19 | - 运行项目于 jetty 上:mvn jetty:run 20 | - 显示版本信息:mvn -version/-v 21 | - 显示详细错误信息:mvn -e 22 | - 验证工程是否正确,所有需要的资源是否可用:mvn validate 23 | - 在集成测试可以运行的环境中处理和发布包:mvn integration-test 24 | - 运行任何检查,验证包是否有效且达到质量标准:mvn verify 25 | - 产生应用需要的任何额外的源代码,如 xdoclet:mvn generate-sources 26 | - 删除再编译:mvn clean install 27 | - 打印出已解决依赖的列表:mvn dependency:resolve 28 | - 打印整个依赖树:mvn dependency:tree 29 | - 想要查看完整的依赖踪迹,包含那些因为冲突或者其它原因而被拒绝引入的构件,打开 Maven 的调试标记运行:mvn install -X 30 | - 构建装配 Maven Assembly 插件是一个用来创建你应用程序特有分发包的插件:mvn install assembly:assembly 31 | - 使用 Hibernate3 插件构造数据库:mvn hibernate3:hbm2ddl 32 | - 使用 help 插件的 describe 目标来输出 Maven Help 插件的信息:mvn help:describe -Dplugin=help 33 | - 使用 Help 插件输出完整的带有参数的目标列:mvn help:describe -Dplugin=help -Dfull 34 | - 获取单个目标的信息,设置 mojo 参数和 plugin 参数。此命令列出了 Compiler 插件的 compile 目标的所有信息:mvn help:describe -Dplugin=compiler -Dmojo=compile -Dfull 35 | - 列出所有 Maven Exec 插件可用的目标:mvn help:describe -Dplugin=exec -Dfull 36 | - 看这个“有效的 (effective)”POM ,它暴露了 Maven 的默认设置:mvn help:effective-pom 37 | - Main Exec 插件让我们能够在不往 classpath 载入适当的依赖的情况下,运行这个程序:mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather 38 | - 打包同时生成源码包:mvn clean source:jar install 39 | 40 | --- 41 | 42 | - 参数1:-DdownloadSources=true(构建项目时下载源码 jar ) 43 | - 参数2:-DdownloadJavadocs=true(构建项目时下载 javadoc 包) 44 | - 参数3:-Dwtpversion=2.0(构建项目时表示是 web 项目,而不是简单的 java 项目) 45 | - 参数4:-Dmaven.test.skip=true( install 时跳过测试) 46 | 47 | --- 48 | 49 | - 示例1:【**eclipse.bat**】 50 | 51 | SET MAVEN_OPTS= -Xms512M -Xmx512M -XX:PermSize=128M -XX:MaxPermSize=128M -XX:ReservedCodeCacheSize=64M 52 | mvn eclipse:clean eclipse:eclipse -DdownloadSources=true -Dwtpversion=2.0 53 | 54 | - 示例2:【**install.bat**】 55 | 56 | SET MAVEN_OPTS= -Xms512M -Xmx512M -XX:PermSize=128M -XX:MaxPermSize=128M -XX:ReservedCodeCacheSize=64M 57 | mvn clean source:jar install -Dmaven.test.skip=true 58 | 59 | 60 | --- 61 | 62 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 63 | 64 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-event.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 MySQL 事件。 2 | 3 | 4 | ## 1 基本语法 5 | 6 | ```sql 7 | CREATE EVENT 8 | [IF NOT EXISTS] 9 | event_name 10 | ON SCHEDULE schedle 11 | [ON COMPLETION [NOT] PRESERVE] 12 | [ENABLE|DESABLE] 13 | [COMMENT 'comment'] 14 | DO sql_statement 15 | 16 | -- 说明: 17 | -- event_name:事件的名称 18 | -- ON SCHEDULE 设定计划任务的方式,有两种: 19 | -- 单次计划任务:AT 时戳 20 | -- 重复的计划任务:EVERY 时间(单位)的数量 时间单位 [STARTS 时戳][ENDS 时戳] 21 | -- 在两种计划任务中,时戳可以是任意的 TIMESTAMP 和 DATETIME 数据类型,要求提供的是将来的时间(大于 CURRENT_TIMESTAMP),而且小于 Unix 时间的最后时间(等于或小于'2037-12-31 23:59:59') 22 | -- 时间单位是关键词:YEAR,MONTH,DAY,HOUR,MINUTE 或者 SECOND 23 | -- [ON COMPLETION [NOT] PRESERVE]:COMPLETION 当单次计划任务执行完毕后或当重复性的计划任务执行到了 ENDS 阶段。而声明 PRESERVE 的作用是使事件在执行完毕后不会被 Drop 掉 24 | -- [ENABLE|DESABLE]:开启/关闭事件 25 | -- [COMMENT 'comment']:注释 26 | -- DO sql_statement:执行的 sql语句 27 | 28 | -- 注意: 29 | -- 全局事件调度器启用状态:SHOW VARIABLES LIKE '%event_scheduler%'; -- ON/OFF 30 | ``` 31 | 32 | ## 2 模板与实例 33 | 34 | ### 2.1 模板 35 | 36 | ```sql 37 | DELIMITER $$ 38 | 39 | -- SET GLOBAL event_scheduler = ON$$ -- required for event to execute but not create 40 | 41 | CREATE /*[DEFINER = { user | CURRENT_USER }]*/ EVENT `E_NAME` 42 | 43 | ON SCHEDULE 44 | /* uncomment the example below you want to use */ 45 | 46 | -- scheduleexample 1: run once 47 | 48 | -- AT 'YYYY-MM-DD HH:MM.SS'/CURRENT_TIMESTAMP { + INTERVAL 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] } 49 | 50 | -- scheduleexample 2: run at intervals forever after creation 51 | 52 | -- EVERY 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] 53 | 54 | -- scheduleexample 3: specified start time, end time and interval for execution 55 | /*EVERY 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] 56 | 57 | STARTS CURRENT_TIMESTAMP/'YYYY-MM-DD HH:MM.SS' { + INTERVAL 1[HOUR|MONTH|WEEK|DAY|MINUTE|...] } 58 | 59 | ENDS CURRENT_TIMESTAMP/'YYYY-MM-DD HH:MM.SS' { + INTERVAL 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] } */ 60 | 61 | /*[ON COMPLETION [NOT] PRESERVE] 62 | [ENABLE | DISABLE] 63 | [COMMENT 'comment']*/ 64 | 65 | DO 66 | BEGIN 67 | (sql_statements) 68 | END$$ 69 | 70 | DELIMITER ; 71 | ``` 72 | 73 | ### 2.2 实例 74 | 75 | ```sql 76 | DELIMITER $$ 77 | 78 | DROP EVENT IF EXISTS `E_TEST`$$ 79 | 80 | CREATE EVENT `E_TEST` 81 | ON SCHEDULE 82 | EVERY 1 DAY 83 | STARTS '2018-01-01 01:00:00' 84 | ON COMPLETION NOT PRESERVE 85 | ENABLE 86 | 87 | DO 88 | BEGIN 89 | -- 执行 sql 90 | INSERT INTO t_log SET createTime = NOW(); 91 | 92 | -- 调用存储过程 93 | CALL p_test(); 94 | END$$ 95 | 96 | DELIMITER ; 97 | ``` 98 | 99 | --- 100 | 101 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 102 | 103 | 104 | --- 105 | 106 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 107 | 108 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/nginx/nginx-fair.md: -------------------------------------------------------------------------------- 1 | 学习在 Nginx 中使用 fair 模块(第三方)来实现负载均衡,fair 采用的不是内建负载均衡使用的轮换的均衡算法,而是**可以根据页面大小、响应时间智能的进行负载均衡**。 2 | 3 | 4 | ## 1 准备工作 5 | 6 | - nginx-upstream-fair 官方下载地址:[https://github.com/gnosek/nginx-upstream-fair](https://github.com/gnosek/nginx-upstream-fair) 7 | - 版本问题:如果使用的 Nginx 版本 >= 1.14.0 时,使用上述模块源码编译时会报错误,需要对源码做一些修改,参考:[https://github.com/gnosek/nginx-upstream-fair/pull/27/commits/ff979a48a0ccb9217437021b5eb9378448c2bd9e](https://github.com/gnosek/nginx-upstream-fair/pull/27/commits/ff979a48a0ccb9217437021b5eb9378448c2bd9e) 。也可以直接下载已经修改好的源码包:[https://files.cnblogs.com/files/ztlsir/nginx-upstream-fair-master.zip](https://files.cnblogs.com/files/ztlsir/nginx-upstream-fair-master.zip) 8 | 9 | ## 2 配置 10 | 11 | - 上传 `nginx-upstream-fair-master.zip` 。 12 | - 解压到 `/usr/local` 目录下。 13 | 14 | ```bash 15 | unzip nginx-upstream-fair-master.zip 16 | ``` 17 | 18 | 1. 未安装过 Nginx 19 | 20 | 具体安装步骤参考: [Nginx 安装 - Linux](https://mp.weixin.qq.com/s/UypOmZsfZmiAz3_FTk3z7Q) 21 | 22 | ```bash 23 | # 只需要在 ./configure 时额外增加 fair 模块 24 | --add-module=/usr/local/nginx-upstream-fair-master 25 | ``` 26 | 27 | 2. 已安装过 Nginx 28 | 29 | 如果已经安装过 Nginx ,又不想重新安装,则可以单独添加 fair 模块。 30 | 31 | ```bash 32 | # 关闭 Nginx 33 | /usr/local/nginx/sbin/nginx -s stop 34 | 35 | # 查看 Nginx 安装时的配置参数,复制备用 36 | /usr/local/nginx/sbin/nginx -V 37 | # configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module ... 38 | 39 | # 进入 nginx-1.16.1 目录 40 | cd /usr/local/nginx-1.16.1 41 | 42 | # 重新执行 cofigure 命令,增加 fair 模块的配置 43 | ./configure --prefix=/usr/local/nginx --error-log-path=/usr/local/nginx/logs/error.log --http-log-path=/usr/local/nginx/logs/access.log --pid-path=/usr/local/nginx/logs/nginx.pid --lock-path=/usr/local/nginx/logs/nginx.lock --http-client-body-temp-path=/usr/local/nginx/temp/client-body --http-proxy-temp-path=/usr/local/nginx/temp/proxy --http-fastcgi-temp-path=/usr/local/nginx/temp/fastcgi --http-uwsgi-temp-path=/usr/local/nginx/temp/uwsgi --http-scgi-temp-path=/usr/local/nginx/temp/scgi --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-file-aio --with-http_realip_module --add-module=/usr/local/nginx-upstream-fair-master 44 | 45 | # 编译(不安装) 46 | make 47 | 48 | # 备份原来的 nginx 命令 49 | cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx-bak 50 | 51 | # 替换原来的 nginx 命令 52 | cp /usr/local/nginx-1.16.1/objs/nginx /usr/local/nginx/sbin/nginx 53 | ``` 54 | 55 | 修改配置文件 `nginx.conf` ,如下: 56 | 57 | ```bash 58 | upstream tomcat_test { 59 | fair; 60 | server 192.168.71.57:8080; 61 | server 192.168.71.57:8081; 62 | } 63 | ``` 64 | 65 | 最后启动 Nginx 服务,验证。**本地采用 tomcat 里面 sleep 的方式测试,结果不对,奇怪!?**。 66 | 67 | --- 68 | 69 | - [Nginx 教程合集](https://mp.weixin.qq.com/s/TdLki2vnjW4hKUz_BgzEHg)(微信左下方**阅读全文**可直达)。 70 | 71 | 72 | --- 73 | 74 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 75 | 76 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-faq.md: -------------------------------------------------------------------------------- 1 | 本文记录 MySQL 安装、启动、运行过程中出现的问题汇总,持续更新中。 2 | 3 | 4 | ## 1 Plugin 'InnoDB' registration as a STORAGE ENGINE failed 5 | 6 | 原因:启动时报错,配置文件修改导致出问题。 7 | 8 | 解决:停服务,删除那些日志文件,再启动服务。 9 | 10 | ```bash 11 | rm -f /usr/local/mysql/data/ib_logfile* 12 | 13 | # 下面的可能也需要删除 14 | rm -f /usr/local/mysql/data/mysql-bin* 15 | ``` 16 | 17 | ## 2 ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) 18 | 19 | 解决:在 my.cnf 配置文件中添加:log_bin_trust_function_creators=1 20 | 21 | ## 3 Waiting for table flush 22 | 23 | 最近遇到一个案例,很多查询被阻塞没有返回结果,使用 show processlist 查看,发现不少 MySQL 线程处于 Waiting for table flush 状态,查询语句一直被阻塞,只能通过 Kill 进程来解决。 24 | 25 | 原因: 26 | 27 | 有些时候,是由于 **`lock table t_test read`** 引起的阻塞。但生产环境中,很多时候可能是由于 **慢查询** 导致 flush table 一直无法关闭该表而一直处于等待状态。 28 | 29 | 另外,网上有个案例,mysqldump 备份时,如果没有使用参数 —single-transaction 或由于同时使用了flush-logs 与 —single-transaction 两个参数也可能引起这样的等待场景,这个两个参数放在一起,会在开始 dump 数据之前先执行一个 FLUSH TABLES 操作。 30 | 31 | 解决: 32 | 33 | 出现 Waiting for table flush 时,我们一般需要找到那些表被 lock 住或那些慢查询导致 flush table 一直在等待而无法关闭该表。然后 Kill 掉对应的线程即可,但是如何精准定位是一个挑战,尤其是生产环境,你使用 show processlist 会看到大量的线程。让你眼花缭乱的,怎么一下子定位问题呢? 34 | 35 | - 对于慢查询引起的其它线程处于 Waiting for table flush 状态的情形: 36 | 37 | 可以查看 show processlist 中 Time 值很大的线程,然后甄别确认后 Kill 掉。有种规律就是这个线程的 Time 列值必定比被阻塞的线程要高,这个就能过滤很多记录。 38 | 39 | - 对于 lock table t_test read 引起的其它线程处于 Waiting for table flush 状态的情形: 40 | 41 | 对于 lock table t_test read 这种情况,这种会话可能处于 Sleep 状态,而且它也不会出现在 show engine innodb status \G 命令的输出信息中。 即使 show open tables where in_use >=1; 能找到是那张表被 lock 住了,但是无法定位到具体的线程(连接),其实这个是一个头痛的问题,可以使用 **MySQL监控利器-Innotop** 。 42 | 43 | 另外,在官方文档中 **`ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE, OPTIMIZE TABLE`** 都能引起这类等待。 44 | 45 | ## 4 Host 'xxx' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 46 | 47 | 原因: 48 | 49 | 同一个 ip 在短时间内产生太多(超过 MySQL 数据库 max_connect_errors 的最大值)中断的数据库连接而导致的阻塞。 50 | 51 | 说明 MySQL 已经得到了大量( max_connect_errors )的主机 'hostname' 的在中途被中断了的连接请求。在 max_connect_errors 次失败请求后, MySQL 认定出错了(像来自一个黑客的攻击),并且阻止该站点进一步的连接,直到某人执行命令 mysqladmin flush-hosts 。 52 | 53 | 解决: 54 | 55 | 1. 提高允许的 max_connect_errors 数量(治标不治本): 56 | 57 | 1. 进入 MySQL 数据库查看 max_connect_errors: show variables like '%max_connect_errors%'; 58 | 2. 修改 max_connect_errors 的数量为 1000: set global max_connect_errors = 1000; 59 | 3. 查看是否修改成功:show variables like '%max_connect_errors%'; 60 | 61 | 2. 使用 mysqladmin flush-hosts 命令清理一下 hosts 文件(不知道 mysqladmin 在哪个目录下可以使用命令查找: whereis mysqladmin ) 62 | 63 | 在查找到的目录下使用命令修改:/usr/local/mysql/bin/mysqladmin flush-hosts -h10.19.11.33 -P3306 -uroot -p123456 64 | 65 | 备注: 66 | 67 | - 其中端口号,用户名,密码都可以根据需要来添加和修改。 68 | - 配置有 master/slave 主从数据库的要把主库和从库都修改一遍的,如果连了多个数据库,则都需要处理。 69 | - 第 2 步也可以在数据库中进行,命令如下: flush hosts; 70 | 71 | --- 72 | 73 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 74 | 75 | 76 | --- 77 | 78 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 79 | 80 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-springsecurity-helloworld.md: -------------------------------------------------------------------------------- 1 | 通过本文体验 Spring Boot 整合 `Spring Security` 。 Spring Security 是 Spring 家族中的一个安全管理框架,但在 Spring Boot 出现之前,使用的没有 Shiro 多,因为在 SSM/SSH 项目中整合 Spring Security 比较麻烦,直到 Spring Boot 的出现。目前关于安全管理框架的整合模式一般有两种,一种是 `SSM/SSH + Shiro` ,另一种是 `Spring Boot/Spring Cloud + Spring Security` ,但并非绝对。 2 | 3 | 4 | ## 1 创建工程 5 | 6 | 创建 Spring Boot 项目 `spring-boot-springsecurity-helloworld` ,添加 `Web/Spring Security` 依赖,如下: 7 | 8 | ![](https://oscimg.oschina.net/oscnet/up-1d23871e606c43a843a073470a40bc2081d.png) 9 | 10 | 最终的依赖如下: 11 | 12 | ```xml 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-security 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-web 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-test 26 | test 27 | 28 | 29 | org.junit.vintage 30 | junit-vintage-engine 31 | 32 | 33 | 34 | 35 | org.springframework.security 36 | spring-security-test 37 | test 38 | 39 | 40 | ``` 41 | 42 | ## 2 测试 43 | 44 | 新增测试类 `HelloController` ,如下: 45 | 46 | ```java 47 | @RestController 48 | public class HelloController { 49 | // 默认用户名为 user ,密码在项目启动时打印在控制台 50 | @GetMapping("/hello") 51 | public String hello() { 52 | return "hello"; 53 | } 54 | } 55 | ``` 56 | 57 | 启动项目之后,浏览器访问 [http://127.0.0.1:8080/hello](http://127.0.0.1:8080/hello) ,会跳转到登录页面(**默认用户名为 user ,密码在项目启动时打印在控制台**),这是因为加入 `Spring Security` 依赖之后,接口就被自动保护起来了。 58 | 59 | 控制台信息:`Using generated security password: 3629761f-b103-4392-8318-c7a8d43ed80d` 60 | 61 | ![](https://oscimg.oschina.net/oscnet/up-bd58156ab4d447fc54ef6f5c96f577a438e.png) 62 | 63 | 当用户从浏览器发送请求访问 `/hello` 接口时,服务端会返回 `302` 响应码,让客户端重定向到 `/login` 页面,用户在 `/login` 页面登录,登陆成功之后,就会自动跳转到 `/hello` 接口。 64 | 65 | 当然也可以用 HTTP 请求工具来测试(如 `Postman` ),将用户信息放在请求头中,这样可以避免重定向到登录页面。 66 | 67 | ![](https://oscimg.oschina.net/oscnet/up-8b7a034114dcb6ddb3863e545dfde6bf891.png) 68 | 69 | 通过以上两种不同的登录方式可以看出 Spring Security 支持两种不同的认证方式: 70 | 71 | - 通过 `form` 表单来认证。 72 | - 通过 `HttpBasic` 来认证。 73 | 74 | --- 75 | 76 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 77 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 78 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-security/spring-boot-springsecurity-helloworld](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-security/spring-boot-springsecurity-helloworld) 79 | 80 | 81 | --- 82 | 83 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 84 | 85 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/prismjs/components/prism-sql.js: -------------------------------------------------------------------------------- 1 | Prism.languages.sql = { 2 | 'comment': { 3 | pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/, 4 | lookbehind: true 5 | }, 6 | 'variable': [ 7 | { 8 | pattern: /@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/, 9 | greedy: true 10 | }, 11 | /@[\w.$]+/ 12 | ], 13 | 'string': { 14 | pattern: /(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/, 15 | greedy: true, 16 | lookbehind: true 17 | }, 18 | 'function': /\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i, // Should we highlight user defined functions too? 19 | 'keyword': /\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i, 20 | 'boolean': /\b(?:TRUE|FALSE|NULL)\b/i, 21 | 'number': /\b0x[\da-f]+\b|\b\d+\.?\d*|\B\.\d+\b/i, 22 | 'operator': /[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i, 23 | 'punctuation': /[;[\]()`,.]/ 24 | }; 25 | -------------------------------------------------------------------------------- /docs/mysql/mysql-bak-timer.md: -------------------------------------------------------------------------------- 1 | 在 Linux 上实现 MySQL 定时备份。 2 | 3 | 4 | ## 1 准备备份的脚本文件 5 | 6 | ### 1.1 创建脚本文件 7 | 8 | ```bash 9 | vi /usr/local/mysqldata/bak/bak.sh 10 | 11 | # !/bin/bash 12 | # db-export variables 13 | EXPORT_DB_HOST="127.0.0.1" 14 | EXPORT_DB_PORT="3306" 15 | EXPORT_DB_USER="root" 16 | EXPORT_DB_PASSWORD="123456" 17 | EXPORT_DB_NAME="db1" 18 | EXPORT_DB_CMD="/usr/local/mysql/bin/mysqldump" 19 | EXPORT_CMD_PREFIX="$EXPORT_DB_CMD -h$EXPORT_DB_HOST -P$EXPORT_DB_PORT -u$EXPORT_DB_USER -p$EXPORT_DB_PASSWORD $EXPORT_DB_NAME" 20 | 21 | # db-import variables 22 | IMPORT_DB_HOST="127.0.0.1" 23 | IMPORT_DB_PORT="3306" 24 | IMPORT_DB_USER="root" 25 | IMPORT_DB_PASSWORD="123456" 26 | IMPORT_DB_NAME="db2" 27 | IMPORT_DB_CMD="/usr/local/mysql/bin/mysql" 28 | IMPORT_CMD_PREFIX="$IMPORT_DB_CMD -h$IMPORT_DB_HOST -P$IMPORT_DB_PORT -u$IMPORT_DB_USER -p$IMPORT_DB_PASSWORD $IMPORT_DB_NAME" 29 | 30 | # bak variables 31 | # BAK_FILE_DATE=`date +%Y%m%d%H%M%S` 32 | BAK_FILE_DATE=`date +%Y%m%d` 33 | BAK_FILE_DIR="/usr/local/mysqldata/bak/$BAK_FILE_DATE" 34 | # BAK_FILE_DIR="/usr/local/mysqldata/bak/$(date +%Y%m%d)" 35 | 36 | # shell script 37 | echo start $(date +%Y-%m-%d_%H:%M:%S). 38 | 39 | mkdir -p $BAK_FILE_DIR 40 | 41 | echo export start. 42 | tabs="tb1 tb2" 43 | echo export tbs... 44 | $EXPORT_CMD_PREFIX $tbs > $BAK_FILE_DIR/tbs.sql 45 | echo export tbs...done. 46 | echo export end. 47 | 48 | echo import start. 49 | echo import tbs... 50 | IMPORT_CMD_PREFIX < $BAK_FILE_DIR/tbs.sql 51 | echo import tbs...done. 52 | echo import end. 53 | 54 | echo end $(date +%Y-%m-%d_%H:%M:%S). 55 | ``` 56 | 57 | ### 1.2 脚本文件添加可执行权限 58 | 59 | ```bash 60 | chmod u+x /usr/local/mysqldata/bak/bak.sh 61 | ``` 62 | 63 | ### 1.3 测试脚本文件 64 | 65 | ```bash 66 | sh /usr/local/mysqldata/bak/bak.sh 67 | # 或者 68 | cd /usr/local/mysqldata/bak 69 | ./bak.sh 70 | ``` 71 | 72 | ## 2 添加计划任务 - crontab 73 | 74 | ```bash 75 | # 编辑任务。会打开 vi,增加上述脚本文件的配置,下面是每1分钟执行一次。实际会对应到 /var/spool/cron 目录下的 root (当前用户)文件中。 76 | crontab -e 77 | */1 * * * * /usr/local/mysqldata/bak/bak.sh 78 | 79 | # 查看任务 80 | crontab -l 81 | */1 * * * * /usr/local/mysqldata/bak/bak.sh。 82 | 83 | # 如果任务执行失败了,可以通过以下命令查看任务日志 84 | tail -f /var/log/cron 85 | 86 | # 输出类似如下: 87 | Oct 31 15:19:18 gridserver crontab[8951]: (root) REPLACE (root) 88 | Oct 31 15:19:18 gridserver crontab[8951]: (root) END EDIT (root) 89 | Oct 31 15:20:01 gridserver crond[4487]: (root) RELOAD (/var/spool/cron/root) 90 | Oct 31 15:20:01 gridserver CROND[8965]: (root) CMD (/usr/lib64/sa/sa1 1 1) 91 | Oct 31 15:20:01 gridserver CROND[8966]: (root) CMD (/usr/local/mysqldata/bak/bak.sh) 92 | Oct 31 15:21:01 gridserver CROND[8980]: (root) CMD (/usr/local/mysqldata/bak/bak.sh) 93 | Oct 31 15:22:01 gridserver CROND[8991]: (root) CMD (/usr/local/mysqldata/bak/bak.sh) 94 | Oct 31 15:23:01 gridserver CROND[9001]: (root) CMD (/usr/local/mysqldata/bak/bak.sh) 95 | Oct 31 15:24:01 gridserver CROND[9015]: (root) CMD (/usr/local/mysqldata/bak/bak.sh) 96 | ``` 97 | 98 | --- 99 | 100 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 101 | 102 | 103 | --- 104 | 105 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 106 | 107 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-staticresources.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Spring Boot 配置静态资源 3 | date: 2019-11-20 10:51:04 4 | categories: Spring Boot 5 | tags: [Spring Boot, 静态资源] 6 | toc: true 7 | --- 8 | 学习 Spring Boot 配置静态资源。 9 | 10 | 11 | ## 1 Spring MVC 配置静态资源 12 | 13 | 先来回顾下在 Spring MVC 中如何配置静态资源。使用 Spring MVC 时,静态资源会被拦截,需要添加额外的配置,一般在 `spring-mvc.xml` 中配置,如下: 14 | 15 | ```xml 16 | 17 | 18 | ``` 19 | 20 | ## 2 Spring Boot 配置静态资源 21 | 22 | ### 2.1 默认位置 23 | 24 | Spring Boot 项目中的静态资源最常见的位置在 `src/main/resources/static` 目录下,其实共有 5 个默认位置能放,重复的资源以优先级高的为准。如下(优先级: 1 > 2 > 3 > 4 > 5 ): 25 | 26 | 1. classpath:/META-INF/resources/ 27 | 2. classpath:/resources/ 28 | 3. classpath:/static/ 29 | 4. classpath:/public/ 30 | 5. / 31 | 32 | 其中,/ 表示**类似** webapp 目录,即 webapp 中的静态文件也可以直接访问。 33 | 34 | 如果在 `src/main/resources/static` 目录下有一个 1.png 的文件,那么访问路径是 `http://127.0.0.1:8080/1.png` ,不需要加 static 。类似 Spring MVC 中的配置 `` ,实际上系统会去 /static/1.png 目录下查找相关的文件。 35 | 36 | ### 2.2 源码解读 37 | 38 | 打开 `org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration` ,找到了静态资源拦截的配置,如下: 39 | 40 | ```java 41 | String staticPathPattern = this.mvcProperties.getStaticPathPattern(); 42 | if (!registry.hasMappingForPattern(staticPathPattern)) { 43 | this.customizeResourceHandlerRegistration( 44 | registry.addResourceHandler(new String[]{staticPathPattern}) 45 | .addResourceLocations( 46 | WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations()) 47 | ) 48 | .setCachePeriod(this.getSeconds(cachePeriod)) 49 | .setCacheControl(cacheControl)); 50 | } 51 | ``` 52 | 53 | 1. `this.mvcProperties.getStaticPathPattern()` 返回 "/**" 。 54 | 2. `this.resourceProperties.getStaticLocations()` 返回 4 个位置 "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" ,然后 `WebMvcAutoConfiguration.getResourceLocations` 又添加了 "/" ,这样总共就是上述 5 个位置。 55 | 56 | ### 2.3 自定义位置 57 | 58 | 上述 5 个是系统默认的位置,有 2 种办法可以实现自定义位置。 59 | 60 | 1. 通过 `application.properties` 配置文件。 61 | 62 | ```xml 63 | # 自定义配置静态资源的匹配规则和路径 64 | # 定义请求 URL 规则 65 | # spring.mvc.static-path-pattern=/** 66 | # 定义资源位置 67 | # spring.resources.static-locations=classpath:/cxy35/ 68 | ``` 69 | 70 | 2. 通过 Java 代码。 71 | 72 | 新增配置类 `WebMvcConfig`, 如下: 73 | 74 | ```java 75 | @Configuration 76 | public class WebMvcConfig implements WebMvcConfigurer { 77 | // 自定义配置静态资源的匹配规则和路径 78 | @Override 79 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 80 | registry.addResourceHandler("/**").addResourceLocations("classpath:/cxy35/"); 81 | } 82 | } 83 | ``` 84 | 85 | --- 86 | 87 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 88 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 89 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-web/spring-boot-staticresources](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-web/spring-boot-staticresources) 90 | 91 | 92 | --- 93 | 94 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 95 | 96 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/nginx/nginx-ssl.md: -------------------------------------------------------------------------------- 1 | Nginx 配置 SSL ,使其支持 HTTPS(自签证书)。 2 | 3 | 4 | ## 1 检查 Nginx 是否支持 SSL 5 | 6 | ```bash 7 | /usr/local/nginx/sbin/nginx -V 8 | ``` 9 | 10 | 查看是否包含 `--with-http_ssl_module` 模块,如果没有,则需要在编译时指定或增加该模块。 11 | 12 | 1. 未安装过 Nginx 13 | 14 | 具体安装步骤参考: [Nginx 安装 - Linux](https://mp.weixin.qq.com/s/UypOmZsfZmiAz3_FTk3z7Q) 15 | 16 | ```bash 17 | # 只需要在 ./configure 时指定 ssl 模块 18 | --with-http_ssl_module 19 | ``` 20 | 21 | 2. 已安装过 Nginx 22 | 23 | 如果已经安装过 Nginx ,又不想重新安装,则可以单独添加 ssl 模块。 24 | 25 | ```bash 26 | # 关闭 Nginx 27 | /usr/local/nginx/sbin/nginx -s stop 28 | 29 | # 查看 Nginx 安装时的配置参数,复制备用 30 | /usr/local/nginx/sbin/nginx -V 31 | # configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_gzip_static_module ... 32 | 33 | # 进入 nginx-1.16.1 目录 34 | cd /usr/local/nginx-1.16.1 35 | 36 | # 重新执行 cofigure 命令,增加 ssl 模块的配置 37 | ./configure \ 38 | --prefix=/usr/local/nginx \ 39 | --error-log-path=/usr/local/nginx/logs/error.log \ 40 | --http-log-path=/usr/local/nginx/logs/access.log \ 41 | --pid-path=/usr/local/nginx/logs/nginx.pid \ 42 | --lock-path=/usr/local/nginx/logs/nginx.lock \ 43 | --http-client-body-temp-path=/usr/local/nginx/temp/client-body \ 44 | --http-proxy-temp-path=/usr/local/nginx/temp/proxy \ 45 | --http-fastcgi-temp-path=/usr/local/nginx/temp/fastcgi \ 46 | --http-uwsgi-temp-path=/usr/local/nginx/temp/uwsgi \ 47 | --http-scgi-temp-path=/usr/local/nginx/temp/scgi \ 48 | --with-http_stub_status_module \ 49 | --with-http_ssl_module \ 50 | --with-http_gzip_static_module \ 51 | --with-file-aio \ 52 | --with-http_realip_module 53 | 54 | # 编译(不安装) 55 | make 56 | 57 | # 备份原来的 nginx 命令 58 | cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx-bak 59 | 60 | # 替换原来的 nginx 命令 61 | cp /usr/local/nginx-1.16.1/objs/nginx /usr/local/nginx/sbin/nginx 62 | ``` 63 | 64 | ## 2 生成证书 65 | 66 | ``` 67 | # 创建存放证书的目录 68 | mkdir /usr/local/nginx/cert 69 | 70 | cd /usr/local/nginx/cert 71 | 72 | # 创建服务器私钥,命令会让你输入一个口令。 73 | openssl genrsa -des3 -out server.key 1024 74 | # 再生成一个不带密码的(非必须) 75 | # openssl rsa -in server.key -out server-nopassword.key 76 | 77 | # 创建签名请求的证书(CSR) 78 | openssl req -new -key server.key -out server.csr 79 | 80 | # 标记证书使用上述私钥和 CSR 81 | openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt 82 | # 再生成一个不带密码的(非必须) 83 | # openssl x509 -req -days 365 -in server.csr -signkey server-nopassword.key -out server-nopassword.crt 84 | ``` 85 | 86 | ## 3 配置 Nginx 87 | 88 | 修改配置文件,开启 ssl ,并指定标记的证书和私钥。 89 | 90 | ```bash 91 | server { 92 | listen 443 ssl; 93 | server_name www.aaa.com; 94 | 95 | ssl_certificate ../cert/server-nopassword.crt; 96 | ssl_certificate_key ../cert/server-nopassword.key; 97 | ssl_session_cache shared:SSL:1m; 98 | ssl_session_timeout 5m; 99 | # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 100 | ssl_ciphers HIGH:!aNULL:!MD5; 101 | ssl_prefer_server_ciphers on; 102 | 103 | #... 104 | } 105 | 106 | server { 107 | listen 80; 108 | server_name localhost-80; 109 | # 将 http 请求重定向到 https 110 | rewrite ^(.*)$ https://$host$1 permanent; 111 | } 112 | ``` 113 | 114 | 重启 Nginx ,此时 http 和 https 都支持。 115 | 116 | --- 117 | 118 | - [Nginx 教程合集](https://mp.weixin.qq.com/s/TdLki2vnjW4hKUz_BgzEHg)(微信左下方**阅读全文**可直达)。 119 | 120 | 121 | --- 122 | 123 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 124 | 125 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mq/mq-high-availability.md: -------------------------------------------------------------------------------- 1 | 如何保证消息队列的高可用? 2 | 3 | ## 1 RabbitMQ 的高可用性 4 | 5 | RabbitMQ 是比较有代表性的,因为是**基于主从(非分布式)**做高可用性的。RabbitMQ 有三种模式:单机模式、普通集群模式(无高可用性)、**镜像集群模式(高可用性)**。 6 | 7 | ### 1.1 单机模式 8 | 9 | 单机模式,就是 Demo 级别的,一般就是你本地启动了玩玩儿的,没人生产用单机模式。 10 | 11 | ### 1.2 普通集群模式(无高可用性) 12 | 13 | 普通集群模式,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。**你创建的 queue,只会放在一个 RabbitMQ 实例上**,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。 14 | 15 | ![](https://oscimg.oschina.net/oscnet/up-9bbb75854c07e153618a99a28879c54fcbe.png) 16 | 17 | 这种方式确实很麻烦,也不怎么好,没做到所谓的分布式,就是个普通集群。因为这导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个 queue 所在实例消费数据,**前者有数据拉取的开销,后者导致单实例性能瓶颈**。 18 | 19 | 而且如果那个放 queue 的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你开启了消息持久化,让 RabbitMQ 落地存储消息的话,消息不一定会丢,得等这个实例恢复了,然后才可以继续从这个 queue 拉取数据。 20 | 21 | 所以这个事儿就比较尴尬了,**这就没有什么所谓的高可用性,这方案主要是提高吞吐量的**,就是说让集群中多个节点来服务某个 queue 的读写操作。 22 | 23 | ### 1.3 镜像集群模式(高可用性) 24 | 25 | 这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,**你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上**,就是说,每个 RabbitMQ 节点都有这个 queue 的一个**完整镜像**,包含 queue 的**全部数据**的意思。然后每次你写消息到 queue 的时候,都会自动把**消息同步**到多个实例的 queue 上。 26 | 27 | ![](https://oscimg.oschina.net/oscnet/up-024d490d9db181e3909871c2a6ec82fff37.png) 28 | 29 | 那么**如何开启这个镜像集群模式呢**?其实很简单,RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是**镜像集群模式的策略**,指定的时候是**可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点**,再次创建 queue 的时候,应用这个策略,就会**自动将数据同步到其他的节点**上去了。 30 | 31 | 这样的话,好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于,第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!第二,这么玩儿,不是分布式的,就没有扩展性可言了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并没有办法线性扩展你的 queue。你想,如果这个 queue 的数据量很大,大到这个机器上的容量无法容纳了,此时该怎么办呢? 32 | 33 | ## 2 Kafka 的高可用性 34 | 35 | Kafka 一个最基本的架构认识:由多个 broker 组成,每个 broker 是一个节点;你创建一个 topic,这个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据。 36 | 37 | 这就是**天然的分布式消息队列**,就是说一个 topic 的数据,是**分散放在多个机器上的,每个机器就放一部分数据**。 38 | 39 | 实际上 RabbmitMQ 之类的,并不是分布式消息队列,它就是传统的消息队列,只不过提供了一些集群、HA(High Availability, 高可用性) 的机制而已,因为无论怎么玩儿,RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。 40 | 41 | Kafka 0.8 以前,是没有 HA 机制的,就是任何一个 broker 宕机了,那个 broker 上的 partition 就废了,没法写也没法读,没有什么高可用性可言。 42 | 43 | 比如说,我们假设创建了一个 topic,指定其 partition 数量是 3 个,分别在三台机器上。但是,如果第二台机器宕机了,会导致这个 topic 的 1/3 的数据就丢了,因此这个是做不到高可用的。 44 | 45 | ![](https://oscimg.oschina.net/oscnet/up-fd10d5ce15f5bf27444acd25913173d28ff.png) 46 | 47 | Kafka 0.8 以后,提供了 HA 机制,就是 replica(复制品) 副本机制。每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本。所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。只能读写 leader?很简单,**要是你可以随意读写每个 follower,那么就要 care 数据一致性的问题**,系统复杂度太高,很容易出问题。Kafka 会均匀地将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。 48 | 49 | ![](https://oscimg.oschina.net/oscnet/up-a435dda1aadfc736a91826d57576d1eb0cd.png) 50 | 51 | 这么搞,就有所谓的**高可用性**了,因为如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的。如果这个宕机的 broker 上面有某个 partition 的 leader,那么此时会从 follower 中**重新选举**一个新的 leader 出来,大家继续读写那个新的 leader 即可。这就有所谓的高可用性了。 52 | 53 | **写数据**的时候,生产者就写 leader,然后 leader 将数据落地写本地磁盘,接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为) 54 | 55 | **消费**的时候,只会从 leader 去读,但是只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候,这个消息才会被消费者读到。 56 | 57 | 看到这里,相信你大致明白了 Kafka 是如何保证高可用机制的了,对吧?不至于一无所知,现场还能给面试官画画图。要是遇上面试官确实是 Kafka 高手,深挖了问,那你只能说不好意思,太深入的你没研究过。 58 | 59 | --- 60 | 61 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 62 | 63 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /lib/docsify-copy-code/dist/docsify-copy-code.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * docsify-copy-code 3 | * v2.1.0 4 | * https://github.com/jperasmus/docsify-copy-code 5 | * (c) 2017-2019 JP Erasmus 6 | * MIT license 7 | */ 8 | !function(){"use strict";function r(o){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o})(o)}!function(o,e){void 0===e&&(e={});var t=e.insertAt;if(o&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=o:c.appendChild(document.createTextNode(o))}}(".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:0}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:-100;top:50%;left:0;padding:.5em .65em;font-size:.825em;opacity:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{opacity:1;-webkit-transform:translate(-115%,-50%);transform:translate(-115%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(o,e){o.ready(function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")})}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,s){o.doneEach(function(){var o=Array.apply(null,document.querySelectorAll("pre[data-lang]")),c={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};s.config.copyCode&&Object.keys(c).forEach(function(t){var n=s.config.copyCode[t];"string"==typeof n?c[t]=n:"object"===r(n)&&Object.keys(n).some(function(o){var e=-1',''.concat(c.buttonText,""),''.concat(c.errorText,""),''.concat(c.successText,""),""].join("");o.forEach(function(o){o.insertAdjacentHTML("beforeend",e)})}),o.mounted(function(){document.querySelector(".content").addEventListener("click",function(o){if(o.target.classList.contains("docsify-copy-code-button")){var e="BUTTON"===o.target.tagName?o.target:o.target.parentNode,t=document.createRange(),n=e.parentNode.querySelector("code"),c=window.getSelection();t.selectNode(n),c.removeAllRanges(),c.addRange(t);try{document.execCommand("copy")&&(e.classList.add("success"),setTimeout(function(){e.classList.remove("success")},1e3))}catch(o){console.error("docsify-copy-code: ".concat(o)),e.classList.add("error"),setTimeout(function(){e.classList.remove("error")},1e3)}"function"==typeof(c=window.getSelection()).removeRange?c.removeRange(t):"function"==typeof c.removeAllRanges&&c.removeAllRanges()}})})}].concat(window.$docsify.plugins||[])}(); 9 | //# sourceMappingURL=docsify-copy-code.min.js.map 10 | -------------------------------------------------------------------------------- /docs/mysql/mysql-bak-xtrabackup.md: -------------------------------------------------------------------------------- 1 | 【TODO-未整理、未实测】MySQL 备份与恢复 - xtrabackup 。 2 | 3 | 4 | 数据库备份恢复之XtraBackup概述及安装部署 5 | 一、xtrabackup工具介绍及备份过程概述 6 | 1.xtrabackup简介: 7 | mysqldump备份方式是采用逻辑备份,其最大的缺陷就是备份和恢复速度都慢,对于一个小于50G的数据库而言,这个速度还是能够接受的,如果数据库非常大,那再使用mysqldump备份就不太适合了。 8 | Xtrabackup是由percona提供的mysql数据库备份工具,据官方介绍,这也是世界上唯一一个开源的能够对innodb和xtradb数据库进行物理热备的工具。 9 | 2.xtrabakcup特点: 10 | 1)备份过程快速,可靠; 11 |   2)备份过程不会打断正在执行的事务(不需要锁表) 12 |   3)能够给予压缩等功能节约磁盘空间和流量。 13 |   4)自动实现备份检验; 14 |   5)还原速度快; 15 |   6)可以进行流传出备份,备份到另外一台机器上。 16 | Xtrabackup中主要有包含两个工具: 17 | 1.innobackupex:是将xtrabackup进行封装的perl脚本,提供了备份myisam表的能力, 18 | 2.xtrabackup:是用于热备innodb,xtradb表中数据的工具,不能备份其他类型的表,也不能备份数据表结构; 19 | 20 | 【xtrabackup 全量备份恢复】 21 | 1. 完全备份 22 | 创建用于备份恢复的用户 pxb 并赋予权限 23 | create user pxb@'localhost' identified by '123456'; 24 | grant reload,process,lock tables,replication client on *.* to pxb[@localhost](https://my.oschina.net/u/570656); 25 | 26 | 创建存放目录 27 | mkdir -pv /data/pxb 28 | 29 | 进行数据库全备(生成全备:/data/pxb/2017-04-24_02-46-11) 30 | innobackupex --defaults-file=/etc/my.cnf --user=pxb --password=123456 --socket=/tmp/mysql.sock  /data/pxb 31 | 32 | 2. 全备恢复 33 | 关闭数据库并删除数据文件 34 | /etc/init.d/mysqld stop 35 | cd /home/mysql 36 | mv data data_bak 37 | mkdir data 38 | 39 | 准备(prepare)一个完全备份:--apply-log ( /data/pxb/2017-04-24\_02-46-11/ 为备份目录,执行之后 xtrabackup\_checkpoints 文件中的 backup_type = full-prepared ) 40 | innobackupex --apply-log /data/pxb/2017-04-24_02-46-11/ 41 | 42 | 执行恢复操作: 43 | innobackupex  --defaults-file=/etc/my.cnf --copy-back --rsync /data/pxb/2017-04-24_02-46-11/ 44 | 45 | 更改 data/ 目录权限并启动mysql: 46 | chown -R mysql:mysql data/ 47 | /etc/init.d/mysqld start 48 | 49 | 验证 50 | 51 | 52 | 【xtrabackup 增量备份恢复】 53 | 我们以之前做的全备为基准,在其基础上做增量备份: 54 | 增量备份1:(以全备为基准:/data/pxb/2017-04-24\_02-46-11/)(生成增量1:/data/pxb/inc/2017-04-28\_01-09-40) 55 | innobackupex --defaults-file=/etc/my.cnf --user=pxb --password=123456 --socket=/tmp/mysql.sock --incremental /data/pxb/inc --incremental-basedir=/data/pxb/2017-04-24_02-46-11/ --parallel=2 56 | 57 | 增量备份2:(以增量1为基准:/data/pxb/inc/2017-04-28\_01-09-40/)(生成增量2:/data/pxb/inc/2017-04-28\_01-27-46) 58 | innobackupex --defaults-file=/etc/my.cnf --user=pxb --password=123456 --socket=/tmp/mysql.sock --incremental /data/pxb/inc --incremental-basedir=/data/pxb/inc/2017-04-28_01-09-40/ --parallel=2 59 | 60 | 增量备份的恢复 61 | 增量备份的恢复需要有3个步骤 62 | 1恢复完全备份 63 | 2恢复增量备份到完全备份(开始恢复的增量备份要添加--redo-only参数,到最后一次增量备份要去掉--redo-only) 64 | 3对整体的完全备份进行恢复,回滚未提交的数据 65 | ##准备一个全备## 66 | innobackupex --apply-log --redo-only /data/pxb/2017-04-24_02-46-11/ 67 | 68 | ##将增量1应用到完全备份## 69 | innobackupex --apply-log --redo-only /data/pxb/2017-04-24\_02-46-11/ --incremental-dir=/data/pxb/inc/2017-04-28\_01-09-40/ 70 | 71 | ##将增量2应用到完全备份,注意不加 --redo-only 参数了## 72 | innobackupex --apply-log /data/pxb/2017-04-24\_02-46-11/ --incremental-dir=/data/pxb/inc/2017-04-28\_01-27-46/ 73 | 74 | ##把所有合在一起的完全备份整体进行一次apply操作,回滚未提交的数据## 75 | innobackupex --apply-log /data/pxb/2017-04-24_02-46-11/ 76 | 77 | 关闭数据库并删除数据文件 78 | /etc/init.d/mysqld stop 79 | cd /home/mysql 80 | mv data data_bak2 81 | mkdir data 82 | 83 | 执行恢复操作: 84 | innobackupex --defaults-file=/etc/my.cnf --copy-back --rsync /data/pxb/2017-04-24_02-46-11/ 85 | 86 | 更改 data/ 目录权限并启动mysql: 87 | chown -R mysql:mysql data/ 88 | /etc/init.d/mysqld start 89 | 90 | 验证 91 | 92 | --- 93 | 94 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 95 | 96 | 97 | --- 98 | 99 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 100 | 101 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-yaml.md: -------------------------------------------------------------------------------- 1 | 学习 Spring Boot 项目中的配置文件( yaml 格式),如: application.yaml 。 2 | 3 | 4 | ## 1 文件位置 5 | 6 | Spring Boot 项目中的配置文件 `application.yaml` 最常见的位置在 `src/main/resources` 目录下,其实共有 4 个默认位置能放,如下(优先级: 1 > 2 > 3 > 4 ): 7 | 8 | 1. 项目根目录下的 config 目录下。 9 | 2. 项目的根目录下。 10 | 3. classpath 目录下的 config 目录下。 11 | 4. classpath 目录下。 12 | 13 | Spring Boot 启动时,默认会从这 4 个位置按顺序去查找相关属性并加载,重复的属性以优先级高的为准。 14 | 15 | 但并非绝对的,我们也可以自定义位置(如:`src/main/resources/cxy35/application.yaml` ),并在项目启动时通过 **`spring.config.location`** 属性来手动的指定配置文件的位置,指定方式如下: 16 | 17 | 1. IntelliJ IDEA 中。 18 | 19 | ![](https://oscimg.oschina.net/oscnet/up-296798d7ad8d1953cef9b6fb55d74abdb9f.png) 20 | 21 | 2. 命令行中。 22 | 23 | ```bash 24 | java -jar spring-boot-yaml-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:/cxy35/ 25 | ``` 26 | 27 | **注意**:通过 **`spring.config.location`** 属性指定时,表示自己重新定义配置文件的位置,项目启动时就按照定义的位置去查找配置文件,这种定义方式会覆盖掉默认的 4 个位置。另外可以通过 **`spring.config.additional-location`** 属性来指定,表示在默认的 4 个位置的基础上,再添加几个位置,新添加的位置的优先级大于原本的位置。 28 | 29 | ## 2 文件名 30 | 31 | Spring Boot 项目中的配置文件默认文件名是 `application.yaml` ,与文件位置类似,也可以自定义,比如叫 `app.yaml` ,并在项目启动时通过 **`spring.config.name`** 属性来手动的指定配置文件的文件名,如:`java -jar spring-boot-yaml-0.0.1-SNAPSHOT.jar --spring.config.name=app` 。 32 | 33 | 当然,配置文件的位置和文件名可以同时自定义。 34 | 35 | ## 3 普通的属性注入 36 | 37 | 首先在 `application.yaml` 配置文件中定义属性: 38 | 39 | ```yaml 40 | book: 41 | id: 1 42 | name: 三国演义 43 | author: 罗贯中 44 | editors: 45 | - 张三 46 | - 李四 47 | chapters: 48 | - id: 1 49 | name: 第一章 桃园结义 50 | - id: 2 51 | name: 第二章 除董卓 52 | ``` 53 | 54 | 再定义一个 Book 和 Chapter 类,并通过 `@Value` 注解将这些属性注入到 Book 对象中(**注意: Book 对象必须要交给 Spring 容器去管理**): 55 | 56 | ```java 57 | @Component 58 | public class Book { 59 | @Value("${book.id}") 60 | private Long id; 61 | @Value("${book.name}") 62 | private String name; 63 | @Value("${book.author}") 64 | private String author; 65 | @Value("${book.editors}") 66 | private List editors; // 普通数组/列表注入 67 | @Value("${book.chapters}") 68 | private List chapters; // 对象数组/列表注入 69 | 70 | // getter/setter 71 | } 72 | ``` 73 | 74 | ```java 75 | public class Chapter { 76 | private Long id; 77 | private String name; 78 | 79 | // getter/setter 80 | } 81 | ``` 82 | 83 | 因为 `application.yaml` 配置文件会被自动加载,所以上述属性可以注入成功,可通过在 controller 或者单元测试中注入 Book 对象来测试。 84 | 85 | yaml 配置目前不支持 `@PropertySource` 注解。 86 | 87 | 上述方式在 Spring 中也可以使用,和 Spring Boot 没有关系。 88 | 89 | ## 4 类型安全的属性注入(推荐) 90 | 91 | 当配置的属性非常多的时候,上述方式工作量大且容易出错,所以就不合适了。在 Spring Boot 中引入了类型安全的属性注入,通过 `@ConfigurationProperties` 注解来实现,如下: 92 | 93 | ```java 94 | @Component 95 | @ConfigurationProperties(prefix = "book") 96 | public class Book { 97 | private Long id; 98 | private String name; 99 | private String author; 100 | private List editors; // 普通数组/列表注入 101 | private List chapters; // 对象数组/列表注入 102 | 103 | // getter/setter 104 | } 105 | ``` 106 | 107 | ## 5 properties 与 yaml 配置的区别 108 | 109 | 1. properties 配置无序,yaml 配置有序。在有些配置中顺序是非常有用的,例如 Spring Cloud Zuul 的配置。 110 | 2. yaml 配置目前不支持 `@PropertySource` 注解。 111 | 112 | --- 113 | 114 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 115 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 116 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-yaml](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-yaml) 117 | 118 | 119 | --- 120 | 121 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 122 | 123 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mysql/mysql-index.md: -------------------------------------------------------------------------------- 1 | 索引是快速搜索的关键,索引的建立对于 MySQL 的高效运行很重要。通过本文学习 MySQL 索引。 2 | 3 | 4 | ## 1 索引类型 5 | 6 | ### 1.1 普通索引 7 | 8 | ```sql 9 | ALTER TABLE `t_user` ADD INDEX `idx_user_username` (`username`); 10 | ``` 11 | 12 | ### 1.2 唯一索引 13 | 14 | 唯一索引的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。 15 | 16 | ```sql 17 | ALTER TABLE `t_user` ADD UNIQUE `idx_user_username` (`username`); 18 | ``` 19 | 20 | ### 1.3 主键索引 21 | 22 | 主键索引是一种特殊的唯一索引,不允许有空值,一般是在建表的时候同时创建主键索引。 23 | 24 | ```sql 25 | CREATE TABLE `t_user` ( 26 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id', 27 |     `username` varchar(16) NOT NULL COMMENT '用户名', 28 |     PRIMARY KEY (`id`) 29 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户'; 30 | ``` 31 | 32 | ### 1.4 组合索引 33 | 34 | ```sql 35 | CREATE TABLE mytable( 36 |     ID INT NOT NULL, 37 |     username VARCHAR(16) NOT NULL, 38 |     city VARCHAR(50) NOT NULL, 39 |     age INT NOT NULL 40 | ); 41 | ``` 42 | 43 | 为了进一步榨取MySQL的效率,就要考虑建立组合索引。就是将 username, city, age 建到一个索引里。 44 | 45 | ```sql 46 | ALTER TABLE mytable ADD INDEX name_city_age (username(10), city, age); 47 | ``` 48 | 49 | 建表时,usernname 长度为 16,这里用 10。这是因为一般情况下名字的长度不会超过 10,这样会加速索引查询速度,还会减少索引文件的大小,提高 INSERT 的更新速度。建立这样的组合索引,其实是相当于分别建立了下面三组组合索引: 50 | 51 | - usernname, city, age 52 | - usernname, city 53 | - usernname 54 | 55 | 为什么没有 city, age 这样的组合索引呢?这是因为 MySQL 组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这三列的查询都会用到该组合索引,下面的几个 SQL 就会用到这个组合索引: 56 | 57 | ```sql 58 | SELECT * FROM mytable WHREE username="admin" AND city="郑州" 59 | SELECT * FROM mytable WHREE username="admin" 60 | ``` 61 | 62 | 而下面几个则不会用到: 63 | 64 | ```sql 65 | SELECT * FROM mytable WHREE age=20 AND city="郑州" 66 | SELECT * FROM mytable WHREE city="郑州" 67 | ``` 68 | 69 | 如果分别在 usernname, city, age 上建立单列索引,让该表有3个单列索引,查询时和上述的组合索引效率也会大不一样,远远低于我们的组合索引。虽然此时有了三个索引,但 MySQL 只能用到其中的那个它认为似乎是最有效率的单列索引。 70 | 71 | ## 2 建立索引的时机 72 | 73 | 到这里我们已经学会了建立索引,那么我们需要在什么情况下建立索引呢? 74 | 75 | 一般来说,在 WHERE 和 JOIN 中出现的列需要建立索引,但也不完全如此,因为 MySQL 只对 <,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE 才会使用索引。 76 | 77 | ```sql 78 | SELECT t.username 79 | FROM mytable t LEFT JOIN mytable2 m 80 | ON t.username=m.username WHERE m.age=20 AND m.city='郑州'; 81 | ``` 82 | 83 | ```sql 84 | -- 会使用索引 85 | SELECT * FROM mytable WHERE username like 'admin%'; 86 | -- 不会使用索引(以通配符 % 和 _ 开头) 87 | SELECT * FROM mytable WHERE username like '%admin'; 88 | ``` 89 | 90 | ## 3 索引的不足之处 91 | 92 | - 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行 INSERT/UPDATE/DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存索引文件。 93 | - 建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。 94 | 95 | 索引只是提高效率的一个因素,如果你的 MySQL 有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。 96 | 97 | ## 4 使用索引的注意事项 98 | 99 | - 索引不会包含有 NULL 值的列。 100 | 101 | 只要列中包含有 NULL 值都将不会被包含在索引中,复合索引中只要有一列含有 NULL 值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为 NULL。 102 | 103 | - 使用短索引 104 | 105 | 对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个 CHAR(255) 的列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作。 106 | 107 | - 索引列排序 108 | 109 | MySQL 查询只使用一个索引,因此如果 where 子句中已经使用了索引的话,那么 order by 中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。 110 | 111 | - like语句操作 112 | 113 | 一般情况下不鼓励使用 like 操作,如果非使用不可,如何使用也是一个问题。`like '%aaa%'` 不会使用索引而 `like 'aaa%'` 可以使用索引。 114 | 115 | - 不要在列上进行运算 116 | 117 | ```sql 118 | select * from users where YEAR(adddate) < 2007; 119 | -- 上述语句将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成: 120 | select * from users where adddate < '2007-01-01'; 121 | ``` 122 | 123 | - 不使用 NOT IN 和 <> 操作 124 | 125 | --- 126 | 127 | - [MySQL 教程合集](https://mp.weixin.qq.com/s/jflrWU62pBtevS62lEIHkQ) 128 | 129 | 130 | --- 131 | 132 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 133 | 134 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-properties.md: -------------------------------------------------------------------------------- 1 | 学习 Spring Boot 项目中的配置文件( properties 格式),如: application.properties 。 2 | 3 | 4 | ## 1 文件位置 5 | 6 | Spring Boot 项目中的配置文件 `application.properties` 最常见的位置在 `src/main/resources` 目录下,其实共有 4 个默认位置能放,如下(优先级: 1 > 2 > 3 > 4 ): 7 | 8 | 1. 项目根目录下的 config 目录下。 9 | 2. 项目的根目录下。 10 | 3. classpath 下的 config 目录下。 11 | 4. classpath 目录下。 12 | 13 | Spring Boot 启动时,默认会从这 4 个位置按顺序去查找相关属性并加载,重复的属性以优先级高的为准。 14 | 15 | 但并非绝对的,我们也可以自定义位置(如:`src/main/resources/cxy35/application.properties` ),并在项目启动时通过 **`spring.config.location`** 属性来手动的指定配置文件的位置,指定方式如下: 16 | 17 | 1. IntelliJ IDEA 中。 18 | 19 | ![](https://oscimg.oschina.net/oscnet/up-6a944dabac87e2b73cbb36225d871b587a9.png) 20 | 21 | 2. 命令行中。 22 | 23 | ```bash 24 | java -jar spring-boot-properties-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:/cxy35/ 25 | ``` 26 | 27 | **注意**:通过 **`spring.config.location`** 属性指定时,表示自己重新定义配置文件的位置,项目启动时就按照定义的位置去查找配置文件,这种定义方式会覆盖掉默认的 4 个位置。另外可以通过 **`spring.config.additional-location`** 属性来指定,表示在默认的 4 个位置的基础上,再添加几个位置,新添加的位置的优先级大于原本的位置。 28 | 29 | ## 2 文件名 30 | 31 | Spring Boot 项目中的配置文件默认文件名是 `application.properties` ,与文件位置类似,也可以自定义,比如叫 `app.properties` ,并在项目启动时通过 **`spring.config.name`** 属性来手动的指定配置文件的文件名,如:`java -jar spring-boot-properties-0.0.1-SNAPSHOT.jar --spring.config.name=app` 。 32 | 33 | 当然,配置文件的位置和文件名可以同时自定义。 34 | 35 | ## 3 普通的属性注入 36 | 37 | 首先在 `application.properties` 配置文件中定义属性: 38 | 39 | ```properties 40 | book.id=99 41 | book.name=三国演义 42 | book.author=罗贯中 43 | ``` 44 | 45 | 再定义一个 Book 类,并通过 `@Value` 注解将这些属性注入到 Book 对象中(**注意: Book 对象必须要交给 Spring 容器去管理**): 46 | 47 | ```java 48 | @Component 49 | public class Book { 50 | @Value("${book.id}") 51 | private Long id; 52 | @Value("${book.name}") 53 | private String name; 54 | @Value("${book.author}") 55 | private String author; 56 | 57 | // getter/setter 58 | } 59 | ``` 60 | 61 | 因为 `application.properties` 配置文件会被自动加载,所以上述属性可以注入成功,可通过在 controller 或者单元测试中注入 Book 对象来测试。 62 | 63 | 但 `application.properties` 我们一般用来放系统相关的配置,可以自定义 properties 文件来存在自定义配置,如新建 `src/main/resources/book.properties` ,内容如下: 64 | 65 | ```properties 66 | book.id=99 67 | book.name=三国演义 68 | book.author=罗贯中 69 | ``` 70 | 71 | 此时 `book.properties` 文件并不会被自动加载,需要在 Book 类中通过 `@PropertySource` 来引入: 72 | 73 | ```java 74 | @Component 75 | @PropertySource("classpath:book.properties") 76 | public class Book { 77 | @Value("${book.id}") 78 | private Long id; 79 | @Value("${book.name}") 80 | private String name; 81 | @Value("${book.author}") 82 | private String author; 83 | 84 | // getter/setter 85 | } 86 | ``` 87 | 88 | 这样 `book.properties` 文件中的属性就可以注入成功了。 89 | 90 | 上述方式在 Spring 中也可以使用,和 Spring Boot 没有关系。 91 | 92 | ## 4 类型安全的属性注入(推荐) 93 | 94 | 当配置的属性非常多的时候,上述方式工作量大且容易出错,所以就不合适了。在 Spring Boot 中引入了类型安全的属性注入,通过 `@ConfigurationProperties` 注解来实现,如下: 95 | 96 | ```java 97 | @Component 98 | @PropertySource("classpath:book.properties") 99 | @ConfigurationProperties(prefix = "book") 100 | public class Book { 101 | private Long id; 102 | private String name; 103 | private String author; 104 | 105 | // getter/setter 106 | } 107 | ``` 108 | 109 | ## 5 properties 与 yaml 配置的区别 110 | 111 | 1. properties 配置无序,yaml 配置有序。在有些配置中顺序是非常有用的,例如 Spring Cloud Zuul 的配置。 112 | 2. yaml 配置目前不支持 `@PropertySource` 注解。 113 | 114 | --- 115 | 116 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 117 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 118 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-properties](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-properties) 119 | 120 | 121 | --- 122 | 123 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 124 | 125 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/todo/pig/pig.txt: -------------------------------------------------------------------------------- 1 | pig 2 | ├── pig-ui -- 前端工程[8080] 3 | ├── pig-auth -- 授权服务提供[3000] 4 | └── pig-common -- 系统公共模块 5 | ├── pig-common-bom -- 全局依赖管理控制 6 | ├── pig-common-core -- 公共工具类核心包 7 | ├── pig-common-datasource -- 动态数据源包 8 | ├── pig-common-job -- xxl-job 封装 9 | ├── pig-common-log -- 日志服务 10 | ├── pig-common-mybatis -- mybatis 扩展封装 11 | ├── pig-common-security -- 安全工具类 12 | ├── pig-common-swagger -- 接口文档 13 | ├── pig-common-feign -- feign 扩展封装 14 | └── pig-common-test -- oauth2.0 单元测试扩展封装 15 | ├── pig-register -- Nacos Server[8848] 16 | ├── pig-gateway -- Spring Cloud Gateway网关[9999] 17 | └── pig-upms -- 通用用户权限管理模块 18 | └── pig-upms-api -- 通用用户权限管理系统公共api模块 19 | └── pig-upms-biz -- 通用用户权限管理系统业务处理模块[4000] 20 | └── pig-visual 21 | └── pig-monitor -- 服务监控 [5001] 22 | ├── pig-codegen -- 图形化代码生成 [5002] 23 | ├── pig-sentinel-dashboard -- 流量高可用 [5003] 24 | └── pig-xxl-job-admin -- 分布式定时任务管理台 [5004] 25 | 26 | 27 | ======================================================== 28 | 29 | 30 | PIGX 31 | ├── PIGX-ui -- 前端工程[8080] 32 | ├── PIGX-auth -- 授权服务提供[3000] 33 | ├── PIGX-common -- 系统公共模块 34 | ├ ├── PIGX-common-bom -- 公共依赖版本 35 | ├ ├── PIGX-common-core -- 公共工具类核心包 36 | ├ ├── PIGX-common-data -- 数据相关 37 | ├ ├── PIGX-common-datasource -- 动态数据源相关 38 | ├ ├── PIGX-common-feign -- feign 通用封装 39 | ├ ├── PIGX-common-gateway -- 动态路由定义 40 | ├ ├── PIGX-common-gray -- 灰度路由控制封装 41 | ├ ├── PIGX-common-job -- 定时任务 42 | ├ ├── PIGX-common-log -- 日志服务 43 | ├ ├── PIGX-common-oss -- 通用文件系统 44 | ├ ├── PIGX-common-security -- 安全工具类 45 | ├ ├── PIGX-common-sentinel -- sentinel分装 46 | ├ ├── PIGX-common-sequence -- 全局发号器 47 | ├ ├── PIGX-common-swagger -- Swagger Api文档生成 48 | ├ ├── PIGX-common-test -- oauth 2.0 单元测试方案 49 | ├ ├── PIGX-common-xss -- xss 安全过滤组件 50 | ├ └── PIGX-common-transaction -- 分布式事务工具包 51 | ├── PIGX-register -- 注册中心、配置中心[8848] 52 | ├── PIGX-gateway -- Spring Cloud Gateway网关[9999] 53 | ├── PIGX-upms -- 通用用户权限管理模块 54 | ├ └── PIGX-upms-api -- 通用用户权限管理系统公共api模块 55 | ├ └── PIGX-upms-biz -- 通用用户权限管理系统业务处理模块[4000] 56 | └── PIGX-visual -- 图形化模块 57 | ├ ├── PIGX-monitor -- Spring Boot Admin监控 [5001] 58 | ├ ├── PIGX-daemon-elastic-job -- 分布式调度中心[elastic-job 版本] 59 | ├ ├── PIGX-daemon-quartz -- 分布式调度中心[quartz] 60 | ├ ├── PIGX-code-gen -- 图形化代码生成[5003] 61 | ├ ├── PIGX-sso-client-demo -- sso 客户端接入示例 62 | ├ ├── PIGX-tx-manager -- PIGX分布式事务解决方案[5004] 63 | ├ ├── PIGX-bi-platform -- 报表在线设计模块[5006] 64 | ├ ├── PIGX-oa-platform -- 工作流模块[5005] 65 | ├ ├── PIGX-pay-platform -- 微信支付宝收单模块[5010] 66 | ├ ├── PIGX-mp-platform -- 微信管理模块[6000] 67 | ├ └── PIGX-sentinel-dashboard -- sentinel 控制台[5005] 68 | 69 | 70 | ======================================================== 71 | 72 | 73 | 依赖关系: 74 | pig-common-security -> pig-common-core/pig-upms-api 75 | pig-gateway -> pig-common-core/pig-common-swagger 76 | pig-auth -> pig-upms-api/pig-common-security/pig-common-feign/pig-common-log 77 | pig-upms-api -> pig-common-core/pig-common-feign/pig-common-mybatis 78 | pig-upms-biz -> pig-upms-api/pig-common-security/pig-common-log/pig-common-mybatis/pig-common-swagger/pig-common-test 79 | 80 | 81 | 核心功能: 82 | pig-gateway: 83 | 验证码过滤器ValidateCodeGatewayFilter(可通过ignore-clients参数配置某些client无需验证) 84 | 前端密码解密过滤器PasswordDecoderFilter 85 | pig-auth: 86 | oauth内部token相关接口TokenEndpoint 87 | token管理相关接口/登出接口PigTokenEndpoint 88 | 登录成功失败等个性化回调 89 | 通过security.oauth2.ignore.urls或@Inner(PermitAllUrlProperties会把这些请求自动加入urls)配置无需鉴权的请求,另外@Inner默认(value=true)仅用于内部服务之间的调用,因此推荐@Inner 90 | pig-upms-api: 91 | 通用实体类 92 | 通用远程调用接口 93 | 需要配置spring.factories 94 | 95 | 96 | 开源版本待完善的功能(可参考ruoyi): 97 | TODO 界面布局设置 98 | TODO 用户管理-导入导出、列表左侧部门树、分配角色 99 | TODO 菜单管理-菜单类型(目录、左菜单、顶菜单) 100 | TODO 角色管理-数据权限、分配用户 101 | TODO 部门管理-导入导出 102 | TODO 字典管理(导出、数据完善、刷新缓存) 103 | TODO 日志管理(登录日志、操作日志) 104 | TODO 岗位管理 105 | TODO 通知公告 106 | TODO 参数管理(刷新缓存) 107 | TODO 表单构建 108 | 109 | 110 | ======================================================== 111 | 112 | 113 | QA: 114 | - demo-biz启动报错:Client not connected,current status:STARTING。解决:服务端需要开放9848和9849端口 115 | -------------------------------------------------------------------------------- /docs/springboot/spring-boot-helloworld.md: -------------------------------------------------------------------------------- 1 | 3 种方式实现 Spring Boot 项目创建。 2 | 3 | 4 | ## 1 官方网站在线创建 5 | 6 | 官方网站:[https://start.spring.io/](https://start.spring.io/),如下: 7 | 8 | ![](https://oscimg.oschina.net/oscnet/up-6f370b0a9e14e2112d0bdb1af9cf87ed782.png) 9 | 10 | 配置说明如下: 11 | 12 | - Project:项目构建工具,这里选择 Maven ,Gradle 一般在 Android 中使用较多。 13 | - Language:开发语言,这里选择 Java 。 14 | - Spring Boot:版本,一般用最新稳定版。 15 | - Project Metadata:Maven 工程相关信息,如项目坐标、项目描述等。Packing 表示项目打包方式,这里选择 jar 包,因为 Spring Boot 内嵌了 Servlet 容器,打成 jar 包即可直接运行,当然也可根据实际情况选择 war 包。Java 表示选择构建的 JDK 版本。 16 | - Dependencies:选择所需要的依赖,这里先加入 web 依赖,可通过关键字搜索。 17 | 18 | 最后点击下面的按钮生成并导出后,用 IntelliJ IDEA 或者 Eclipse 打开继续开发。 19 | 20 | ## 2 IDE 创建 21 | 22 | 这里演示使用 IntelliJ IDEA 作为 IDE 来创建。 23 | 24 | File -> New Project -> Spring Initializr 。 25 | 26 | 可以看到这里实际上也是用了官方网站的地址来创建,只是 IntelliJ IDEA 把里面的东西集成进来了。 27 | 28 | ![](https://oscimg.oschina.net/oscnet/up-545be15a0deb538585002702c2ef6195e8b.png) 29 | 30 | 填写 Maven 工程相关信息: 31 | 32 | ![](https://oscimg.oschina.net/oscnet/up-545be15a0deb538585002702c2ef6195e8b.png) 33 | 34 | 选择依赖: 35 | 36 | ![](https://oscimg.oschina.net/oscnet/up-1ecbd1f416c18b57a5a0561044700baa7d7.png) 37 | 38 | 填写项目相关信息: 39 | 40 | ![](https://oscimg.oschina.net/oscnet/up-fbe4918c67e4d4e10c28a61613857ef80e5.png) 41 | 42 | ## 3 Maven 创建 43 | 44 | File -> New Project -> Maven 。 45 | 46 | 选择项目骨架(我这里不选择,有兴趣的可以试下里面的 Spring Boot 相关项目骨架): 47 | 48 | ![](https://oscimg.oschina.net/oscnet/up-295b7dcaf127120d53ac40a77fa5d90fa92.png) 49 | 50 | 填写 Maven 工程相关信息: 51 | 52 | ![](https://oscimg.oschina.net/oscnet/up-48a4e2e0780feb7a78f4361a076768214d7.png) 53 | 54 | 填写项目相关信息: 55 | 56 | ![](https://oscimg.oschina.net/oscnet/up-fbe4918c67e4d4e10c28a61613857ef80e5.png) 57 | 58 | 创建完成后,打开 pom.xml 添加依赖: 59 | 60 | ```xml 61 | 62 | org.springframework.boot 63 | spring-boot-starter-parent 64 | 2.2.4.RELEASE 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-web 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | org.junit.vintage 80 | junit-vintage-engine 81 | 82 | 83 | 84 | 85 | ``` 86 | 87 | 接着在 `com.cxy35.sample.springboot.helloworld` 包中创建启动类 `SpringBootHelloworldApplication.java` : 88 | 89 | ```java 90 | @SpringBootApplication 91 | public class SpringBootHelloworldApplication { 92 | 93 | public static void main(String[] args) { 94 | SpringApplication.run(SpringBootHelloworldApplication.class, args); 95 | } 96 | 97 | } 98 | ``` 99 | 100 | ## 4 启动项目测试 101 | 102 | 在 `com.cxy35.sample.springboot.helloworld.controller` 包中创建测试类 `HelloController.java` : 103 | 104 | ```java 105 | @RestController 106 | public class HelloController { 107 | @GetMapping("/hello") 108 | public String hello() { 109 | return "hello spring boot!"; 110 | } 111 | } 112 | ``` 113 | 114 | 运行 `SpringBootHelloworldApplication.java` 中的 main 方法启动项目,浏览器访问 [http://127.0.0.1:8080/hello](http://127.0.0.1:8080/hello) 测试。 115 | 116 | 最终的项目结构如下: 117 | 118 | ![](https://oscimg.oschina.net/oscnet/up-cbb2c4870227aa2d08aa1b2b97e273e8cc7.png) 119 | 120 | --- 121 | 122 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 123 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 124 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-helloworld](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-helloworld) 125 | 126 | 127 | --- 128 | 129 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 130 | 131 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/mycat-overview.md: -------------------------------------------------------------------------------- 1 | 通过本文学习 Mycat 的介绍、架构、原理、应用场景等。 2 | 3 | 4 | ## 1 Mycat 介绍 5 | 6 | Mycat 是什么?从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了 MySQL 协议的的 Server ,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生( Native )协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信。 Mycat 的前身是阿里巴巴的 Cobar ,**其核心功能是分表分库,即将一个大表水平分割为 N 个小表,存储在后端 MySQL 服务器里或者其他数据库里。可以这样理解:数据库是对底层存储文件的抽象,而 Mycat 是对数据库的抽象。** 7 | 8 | Mycat 发展到目前的版本,已经不是一个单纯的 MySQL 代理了,它的后端可以支持 **MySQL、SQL Server、Oracle、DB2、PostgreSQL** 等主流数据库,也支持 **MongoDB** 这种新型 NoSQL 方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在 Mycat 里,都是一个传统的数据库表,支持标准的 SQL 语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。 9 | 10 | 在测试阶段,可以将一个表定义为任何一种 Mycat 支持的存储方式,比如 MySQL 的 MyASIM 表、内存表、或者 MongoDB、LevelDB 以及号称是世界上最快的内存数据库 MemSQL 上。试想一下,用户表存放在 MemSQL 上,大量读频率远超过写频率的数据如订单的快照数据存放于 InnoDB 中,一些日志数据存放于 MongoDB 中,而且还能把 Oracle 的表跟 MySQL 的表做关联查询,你是否有一种不能呼吸的感觉?而未来,还能通过 Mycat 自动将一些计算分析后的数据灌入到 Hadoop 中,并能用 Mycat+Storm/Spark Stream 引擎做大规模数据分析,看到这里,你大概明白了, Mycat 是什么? Mycat 就是 BigSQL,Big Data On SQL Database。 11 | 12 | 对于 DBA 来说,可以这么理解 Mycat:Mycat 就是 MySQL Server ,而 Mycat 后面连接的 MySQL Server ,就好象是 MySQL 的存储引擎,如 InnoDB,MyISAM 等,因此,Mycat 本身并不存储数据,数据是在后端的 MySQL 上存储的,因此数据可靠性以及事务等都是 MySQL 保证的,简单的说, Mycat 就是 MySQL 最佳伴侣,它在一定程度上让 MySQL 拥有了能跟 Oracle PK 的能力。 13 | 14 | 对于软件工程师来说,可以这么理解 Mycat:Mycat 就是一个近似等于 MySQL 的数据库服务器,你可以用连接 MySQL 的方式去连接 Mycat (除了端口不同,默认的 Mycat 端口是 8066 而非 MySQL 的 3306 ,因此需要在连接字符串上增加端口信息),大多数情况下,可以用你熟悉的对象映射框架使用 Mycat ,但建议对于分片表,尽量使用基础的 SQL 语句,因为这样能达到最佳性能,特别是几千万甚至几百亿条记录的情况下。 15 | 16 | 对于架构师来说,可以这么理解 Mycat:Mycat 是一个强大的数据库中间件,不仅仅可以用作读写分离、以及分表分库、容灾备份,而且可以用于多租户应用开发、云平台基础设施、让你的架构具备很强的适应性和灵活性,借助于即将发布的 Mycat 智能优化模块,系统的数据访问瓶颈和热点一目了然,根据这些统计分析数据,你可以自动或手工调整后端存储,将不同的表映射到不同存储引擎上,而整个应用的代码一行也不用改变。 17 | 18 | 当前是个大数据的时代,但究竟怎样规模的数据适合数据库系统呢?对此,国外有一个数据库领域的权威人士说了一个结论:千亿以下的数据规模仍然是数据库领域的专长,而 Hadoop 等这种系统,更适合的是千亿以上的规模。所以, Mycat 适合 1000 亿条以下的单表规模,如果你的数据超过了这个规模,请投靠 Mycat Plus 吧! 19 | 20 | ## 2 Mycat 架构 21 | 22 | ![](https://oscimg.oschina.net/oscnet/966eebd4e2c05a6aaf284174579c6a80bc3.jpg) 23 | 24 | ## 3 Mycat 原理  25 | 26 | Mycat 的原理并不复杂,复杂的是代码,如果代码也不复杂,那么早就成为一个传说了。 Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的SQL语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。 27 | 28 | ![](https://static.oschina.net/uploads/space/2018/0119/165025_L8Hw_593078.png) 29 | 30 | 上述图片里, Orders 表被分为三个分片 datanode (简称 dn ),这三个分片是分布在两台 MySQL Server 上( DataHost ),即 datanode=database@datahost 方式,因此你可以用一台到N台服务器来分片,分片规则为( sharding rule )典型的字符串枚举分片规则,一个规则的定义是分片字段( sharding column )+分片函数( rule function ),这里的分片字段为 prov 而分片函数为字符串枚举方式。 31 | 32 | 当 Mycat 收到一个 SQL 时,会先解析这个 SQL ,查找涉及到的表,然后看此表的定义,如果有分片规则,则获取到 SQL 里分片字段的值,并匹配分片函数,得到该 SQL 对应的分片列表,然后将 SQL 发往这些分片去执行,最后收集和处理所有分片返回的结果数据,并输出到客户端。以 `select * from Orders where prov=?` 语句为例,查到 prov=wuhan ,按照分片函数, wuhan 返回 dn1 ,于是 SQL 就发给了 MySQL1 ,去取 DB1 上的查询结果,并返回给用户。 33 | 34 | 如果上述 SQL 改为 `select * from Orders where prov in (‘wuhan’,‘beijing’)` ,那么, SQL 就会发给 MySQL1 与 MySQL2 去执行,然后结果集合并后输出给用户。但通常业务中我们的 SQL 会有 Order By 以及 Limit 翻页语法,此时就涉及到结果集在 Mycat 端的二次处理,这部分的代码也比较复杂,而最复杂的则属两个表的 Jion 问题,为此, Mycat 提出了创新性的 ER 分片、全局表、HBT(Human Brain Tech) 人工智能的 Catlet 、以及结合 Storm/Spark 引擎等十八般武艺的解决办法,从而成为目前业界最强大的方案,这就是开源的力量! 35 | 36 | ## 4 Mycat 应用场景 37 | 38 | Mycat 发展到现在,适用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是几个典型的应用场景: 39 | 40 | - 单纯的**读写分离**,此时配置最为简单,支持读写分离,主从切换。 41 | - **分表分库**,对于超过 1000 万的表进行分片,最大支持 1000 亿的单表分片。 42 | - **多租户应用**,每个应用一个库,但应用程序只连接 Mycat ,从而不改造程序本身,实现多租户化。 43 | - **报表系统**,借助于 Mycat 的分表能力,处理大规模报表的统计。 44 | - 替代 Hbase ,**分析大数据**。 45 | - 作为**海量数据实时查询**的一种简单有效方案,比如 100 亿条频繁查询的记录需要在 3 秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时 Mycat 可能是最简单有效的选择。 46 | 47 | ![](https://oscimg.oschina.net/oscnet/4aecc87084f7279620ed24724e95d8956f1.jpg) 48 | 49 | ![](https://oscimg.oschina.net/oscnet/1db940ad9ca3b165593d8f7b4b38707fa16.jpg) 50 | 51 | ## 5 Mycat 长期路线图 52 | 53 | - 强化分布式数据库中间件的方面的功能,使之具备丰富的插件、强大的数据库智能优化功能、全面的系统监控能力、以及方便的数据运维工具,实现在线数据扩容、迁移等高级功能。 54 | - 进一步挺进大数据计算领域,深度结合 Spark Stream 和 Storm 等分布式实时流引擎,能够完成快速的巨表关联、排序、分组聚合等 OLAP 方向的能力,并集成一些热门常用的实时分析算法,让工程师以及 DBA 们更容易用 Mycat 实现一些高级数据分析处理功能。 55 | - 不断强化 Mycat 开源社区的技术水平,吸引更多的 IT 技术专家,使得 Mycat 社区成为中国的 Apache ,并将 Mycat 推到 Apache 基金会,成为国内顶尖开源项目,最终能够让一部分志愿者成为专职的 Mycat 开发者,荣耀跟实力一起提升。 56 | - 依托 Mycat 社区,聚集 100 个 CXO 级别的精英,众筹建设亲亲山庄, Mycat 社区+亲亲山庄=中国最大 IT O2O 社区。 57 | 58 | 59 | --- 60 | 61 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 62 | 63 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mq/mq-why.md: -------------------------------------------------------------------------------- 1 | 为什么使用消息队列?消息队列有什么优点和缺点?ActiveMQ、RabbitMQ、RocketMQ、Kafka 都有什么优点和缺点? 2 | 3 | ## 1 优点 4 | 5 | 消息队列常见的优点有 3 个:**系统解耦、异步调用、流量削峰**,在特殊的场景有特殊的用处。 6 | 7 | ### 1.1 系统解耦 8 | 9 | 看这么个场景:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃...... 10 | 11 | ![](https://oscimg.oschina.net/oscnet/up-7c9d9896fd1b9bb3739ee1d65c1c21cd3c2.png) 12 | 13 | 在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊! 14 | 15 | 如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。 16 | 17 | ![](https://oscimg.oschina.net/oscnet/up-b1fb99f363b12372e87c9f883acffa2d5e9.png) 18 | 19 | **总结**:通过一个 MQ,Pub/Sub 发布/订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。 20 | 21 | ### 1.2 异步调用 22 | 23 | 再来看一个场景:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。 24 | 25 | ![](https://oscimg.oschina.net/oscnet/up-b281c60da801dc60f01fddeb4525477b001.png) 26 | 27 | 一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。 28 | 29 | 如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快! 30 | 31 | ![](https://oscimg.oschina.net/oscnet/up-c45928861050b3dce01b985fcf52efdcf72.png) 32 | 33 | 比如电商系统中的某个业务场景:![](https://oscimg.oschina.net/oscnet/up-17c85d777ac4120d081f9ba7bbab65fd4d3.png) 34 | 35 | ### 1.3 流量削峰 36 | 37 | 每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL 的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。 38 | 39 | 一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,导致系统崩溃,用户也就没法再使用系统了。 40 | 41 | 但是高峰期一过,到了下午的时候,就成了低峰期,可能也就 1w 的用户同时在网站上操作,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力。 42 | 43 | ![](https://oscimg.oschina.net/oscnet/up-0c28692dd0e69c157cdf7ae5699f49dd35d.png) 44 | 45 | 如果使用 MQ,每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。而 MQ 每秒钟 5k 个请求进来,就 2k 个请求出去,结果就导致在中午高峰期(1 个小时),可能有几十万甚至几百万的请求积压在 MQ 中。 46 | 47 | ![](https://oscimg.oschina.net/oscnet/up-1f8a1b261a02ec1d864dd63c1edf59c5679.png) 48 | 49 | 这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是 A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给解决掉。 50 | 51 | ## 2 缺点 52 | 53 | 1. **系统可用性降低** 54 | 55 | 系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?[如何保证消息队列的高可用?]() 56 | 57 | 2. **系统复杂度提高** 58 | 59 | 硬生生加个 MQ 进来,[如何保证消息不被重复消费?]()[如何保证消息的可靠性传输?]()[如何保证消息的顺序性?]()头大头大,问题一大堆,痛苦不已。 60 | 61 | 3. **一致性问题** 62 | 63 | A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。 64 | 65 | ## 3 技术选型 66 | 67 | |**特性**|**ActiveMQ**|**RabbitMQ**|**RocketMQ**|**Kafka**| 68 | |:-|:-|:-|:-|:-| 69 | |单机吞吐量|万级,比 RocketMQ、Kafka 低一个数量级|同 ActiveMQ|10 万级,支撑高吞吐|10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景| 70 | |topic 数量对吞吐量的影响|||topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic|topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源| 71 | |时效性|ms 级|微秒级,这是 RabbitMQ 的一大特点,延迟最低|ms 级|延迟在 ms 级以内| 72 | |可用性|高,基于主从架构实现高可用|同 ActiveMQ|非常高,分布式架构|非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用| 73 | |消息可靠性|有较低的概率丢失数据|基本不丢|经过参数优化配置,可以做到 0 丢失|同 RocketMQ| 74 | |功能支持|MQ 领域的功能极其完备|基于 erlang 开发,并发能力很强,性能极好,延时很低|MQ 功能较为完善,还是分布式的,扩展性好|功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用| 75 | 76 | **消息分发策略的机制和对比**: 77 | 78 | ![](https://oscimg.oschina.net/oscnet/up-2e845048bd29b43f86c2c547eba6125d8dd.JPEG) 79 | 80 | 综上,各种对比之后,有如下建议: 81 | 82 | 一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了; 83 | 84 | 后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高; 85 | 86 | 不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。 87 | 88 | 所以**中小型公司**,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;**大型公司**,基础架构研发实力较强,用 RocketMQ 是很好的选择。 89 | 90 | 如果是**大数据领域**的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。 91 | 92 | 93 | --- 94 | 95 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 96 | 97 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springcloud/spring-cloud-bus.md: -------------------------------------------------------------------------------- 1 | 学习在 Spring Cloud 中使用 Bus 实现消息总线,包括配置文件自动批量刷新、逐个刷新等功能。 2 | 3 | 4 | ## 1 概述 5 | 6 | Spring Cloud Bus 通过轻量级的消息代理连接各个微服务,可以用来广播配置文件的更改,或者管理服务监控。在 [Spring Cloud Config 分布式配置中心](https://mp.weixin.qq.com/s/QRO0WBoPS_13IdK_VoAWzA) 一文中,我们提到,**当配置文件发生变化之后, config-server 可以及时感知到变化,但是 config-client 不会及时感知到变化**,默认情况下, config-client 只有重启才能加载到最新的配置文件。当时我们在 config-client 中结合 actuator 中的 refresh 来解决了这个问题,但是,如果 config-client 数量很多的时候,这种方案就显得很繁琐了,不合适。本文我们结合 Spring Cloud Bus 来解决这一问题。 7 | 8 | ## 2 基本使用 9 | 10 | 将 spring-cloud-config 中的代码(包括 config-server/config-client )拷贝一份到 spring-cloud-bus 中(重命名为 bus-config-server/bus-config-client ),在此基础上进行修改。 11 | 12 | 首先,安装并启动 RabbitMQ 。 13 | 14 | 接着,在 bus-config-server 和 bus-config-client 中分别添加 `Spring Cloud Bus` 依赖: 15 | 16 | ```xml 17 | 18 | org.springframework.cloud 19 | spring-cloud-starter-bus-amqp 20 | 21 | ``` 22 | 23 | 然后,在 bus-config-server 和 bus-config-client 中分别添加 RabbitMQ 配置: 24 | 25 | ```properties 26 | # 配置 RabbitMQ 27 | spring.rabbitmq.host=127.0.0.1 28 | spring.rabbitmq.port=5672 29 | spring.rabbitmq.username=guest 30 | spring.rabbitmq.password=guest 31 | ``` 32 | 33 | --- 34 | 35 | 同时,给 bus-config-server 添加 actuator 依赖,将提供刷新接口: 36 | 37 | ```xml 38 | 39 | org.springframework.boot 40 | spring-boot-starter-actuator 41 | 42 | ``` 43 | 44 | 然后,修改 bus-config-server 中的 bootstrap.properties 配置文件,使 bus-refresh 端点暴露出来: 45 | 46 | ```properties 47 | # 暴露 bus-refresh 端点 48 | management.endpoints.web.exposure.include=bus-refresh 49 | ``` 50 | 51 | --- 52 | 53 | 由于给 bus-config-server 中的所有接口都添加了保护,所以刷新接口将无法直接访问,此时,可以通过修改 Security 配置,对端点的权限做出修改: 54 | 55 | ```java 56 | @Configuration 57 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 58 | @Override 59 | protected void configure(HttpSecurity http) throws Exception { 60 | http.authorizeRequests() 61 | .anyRequest().authenticated() 62 | .and() 63 | .httpBasic() 64 | .and() 65 | .csrf().disable(); 66 | } 67 | } 68 | ``` 69 | 70 | 在这段配置中,开启了 HttpBasic 登录,这样,在发送刷新请求时,就可以直接通过 HttpBasic 配置认证信息了。 71 | 72 | 最后分别启动 Eureka Server/bus-config-server/bus-config-client ,然后修改配置信息提交到 GitHub,访问 [http://127.0.0.1:8082/hello](http://127.0.0.1:8082/hello) 不会有变化。然后,调用 [http://127.0.0.1:8080/actuator/bus-refresh](http://127.0.0.1:8080/actuator/bus-refresh) [POST]。再次访问 [http://127.0.0.1:8082/hello](http://127.0.0.1:8082/hello) 发现有变化。 73 | 74 | ![](https://oscimg.oschina.net/oscnet/up-36b329d7776dd00530af075ab743f2fcf9f.png) 75 | 76 | 这个 POST 是针对 bus-config-server 的, bus-config-server 会把这个刷新的指令传到 RabbitMQ ,然后 RabbitMQ 再把指令传给各个 bus-config-client ,实现了配置文件自动批量刷新。 77 | 78 | --- 79 | 80 | 如果更新配置文件之后,不希望每一个微服务 bus-config-client 都去刷新配置文件,那么可以通过如下配置解决问题。 81 | 82 | 首先,修改 bus-config-client 中的 bootstrap.properties 配置文件,给每一个 bus-config-client 添加一个 instance-id: 83 | 84 | ```properties 85 | eureka.instance.instance-id=${spring.application.name}:${server.port} 86 | ``` 87 | 88 | 然后,对 bus-config-client 进行打包,打包完成后,通过如下命令启动两个 bus-config-client 实例: 89 | 90 | ``` 91 | java -jar bus-config-client-0.0.1-SNAPSHOT.jar --server.port=8082 92 | java -jar bus-config-client-0.0.1-SNAPSHOT.jar --server.port=8083 93 | ``` 94 | 95 | 修改配置文件,并且提交到 GitHub 之后,可以通过如下方式只刷新某一个微服务,例如只刷新 8082 的服务。 96 | 97 | [http://127.0.0.1:8080/actuator/bus-refresh/bus-config-client:8082](http://127.0.0.1:8080/actuator/bus-refresh/bus-config-client:8082) [POST] 98 | 99 | 其中 bus-config-client:8082 表示服务的 instance-id 。 100 | 101 | ![](https://oscimg.oschina.net/oscnet/up-6c0e3dde51c83a412c15a5396868582ae31.png) 102 | 103 | --- 104 | 105 | - [Spring Cloud 教程合集](https://mp.weixin.qq.com/s/SBmcs2bxumhNz4kky1pl-A)(微信左下方**阅读全文**可直达)。 106 | - Spring Cloud 教程合集示例代码:[https://github.com/cxy35/spring-cloud-samples](https://github.com/cxy35/spring-cloud-samples) 107 | - 本文示例代码:[https://github.com/cxy35/spring-cloud-samples/tree/master/spring-cloud-bus](https://github.com/cxy35/spring-cloud-samples/tree/master/spring-cloud-bus) 108 | 109 | 110 | --- 111 | 112 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 113 | 114 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/redis/redis-install.md: -------------------------------------------------------------------------------- 1 | 手把手带你使用多种姿势安装 `Redis` 。 2 | 3 | 4 | ## 1 简介 5 | 6 | Redis(Remote Dictionary Service) 是一个使用 ANSI C 编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。从 2015 年 6 月开始,Redis 的开发由 Redis Labs 赞助,而 2013 年 5 月至 2015 年 6 月期间,其开发由 Pivotal 赞助。在 2013 年 5 月之前,其开发由 VMware 赞助。根据月度排行网站 [DB-Engines.com](https://db-engines.com) 的数据显示,Redis 是最流行的键值对存储数据库。 7 | 8 | 很多人想到 Redis ,就想到缓存。但实际上 Redis 除了缓存之外,还有许多更加丰富的使用场景,比如分布式锁、限流等。 9 | 10 | 主要有如下特点: 11 | 12 | - Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,不会造成数据丢失。 13 | - Redis 支持多种不同的数据结构类型之间的映射,包括简单的 key/value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。 14 | - Redis 支持 master-slave 模式的数据备份。 15 | 16 | 主要有如下功能: 17 | 18 | - 内存存储和持久化:redis 支持异步将内存中的数据写到硬盘上,在持久化的同时不影响继续服务。 19 | - 取最新 N 个数据的操作,如:可以将最新的 10 条评论的 ID 放在 Redis 的 List 集合里面。 20 | - 数据可以设置过期时间。 21 | - 自带发布、订阅消息系统。 22 | - 定时器、计数器。 23 | 24 | ## 2 安装 25 | 26 | 主要有 4 种方式获取 Redis : 27 | 28 | 1. 编译安装(**推荐**)。 29 | 2. 使用 Docker 安装。 30 | 3. 直接安装。 31 | 4. 在线体验。 32 | 33 | ### 2.1 编译安装(推荐) 34 | 35 | #### 2.1.1 下载安装包 36 | 37 | - 访问 [https://redis.io/download/](https://redis.io/download/) 下载对应**稳定版**的安装包,如:`redis-6.2.7.tar.gz`。 38 | - 上传到服务器的 `/usr/local/mydata/temp` 目录下。如果没有,则手动新建。 39 | 40 | #### 2.1.2 安装 gcc 环境 41 | 42 | ```bash 43 | # 安装 gcc-c++ 44 | yum install gcc-c++ 45 | ``` 46 | 47 | #### 2.1.2 安装 48 | 49 | ```bash 50 | # 解压 51 | # 其中 redis-6.2.7.tar.gz 换成实际的名称 52 | cd /usr/local/mydata/temp 53 | tar -xvzf redis-6.2.7.tar.gz 54 | 55 | # 进入 redis-6.2.7 目录,其中 redis-6.2.7 换成实际的名称 56 | cd /usr/local/mydata/temp/redis-6.2.7 57 | # 创建安装目录 58 | mkdir -p /usr/local/mydata/soft/redis 59 | # 编译安装,指定安装目录 60 | make 61 | make PREFIX=/usr/local/mydata/soft/redis install 62 | 63 | # 执行完成之后,在 Redis 安装目录下( /usr/local/mydata/soft/redis )多了 bin 目录 64 | # 拷贝配置文件 65 | cp /usr/local/mydata/temp/redis-6.2.7/redis.conf /usr/local/mydata/soft/redis 66 | ``` 67 | 68 | ### 2.2 使用 Docker 安装 69 | 70 | Docker 安装好之后,启动 Docker ,直接运行安装命令即可: 71 | 72 | ```bash 73 | docker run --name redis -d -p 6379:6379 redis --requirepass 123456 74 | ``` 75 | 76 | Docker 上的 Redis 启动成功之后,可以从宿主机上连接(前提是宿主机上存在 redis-cli): 77 | 78 | ```bash 79 | redis-cli -a 123456 80 | ``` 81 | 82 | 如果宿主机上没有安装 Redis ,那么也可以进入到 Docker 容器中去操作 Redis: 83 | 84 | ```bash 85 | docker exec -it redis redis-cli -a 123456 86 | ``` 87 | 88 | ### 2.3 直接安装 89 | 90 | - CentOS:`yum install redis` 91 | - Ubuntu:`apt-get install redis` 92 | - Mac:`brew install redis` 93 | 94 | ### 2.4 在线体验 95 | 96 | 在线体验地址:[http://try.redis.io/](http://try.redis.io/) 97 | 98 | ## 3 配置 99 | 100 | 执行:`vi /usr/local/mydata/soft/redis/redis.conf` 打开配置文件,按需修改配置。 101 | 102 | - 服务后台启动 103 | 104 | 将 `daemonize no` 改成 `daemonize yes` 。 105 | 106 | - 配置密码 107 | 108 | 取消注释,并修改密码:`requirepass 123456` ,之后可用 `redis-cli` 连接之后再用 `auth 123456` 完成认证(推荐),或者通过 `redis-cli -a 123456` 直接连接。 109 | 110 | - 远程登录 111 | 112 | 如果需要除本机外其他网格也能连接,则需要在配置文件中将 `bind 127.0.0.1` 注释掉。 113 | 114 | ## 4 启动 115 | 116 | 通过 `redis-server` 命令启动 `Redis` ,如下: 117 | 118 | ```bash 119 | # 切换到 Redis 安装目录 120 | cd /usr/local/mydata/soft/redis 121 | 122 | # 启动 123 | /usr/local/mydata/soft/redis/bin/redis-server /usr/local/mydata/soft/redis/redis.conf 124 | 125 | # 检查是否启动 126 | ps -ef|grep redis 127 | # root 17529 1 0 15:15 ? 00:00:00 /usr/local/mydata/soft/redis/bin/redis-server 127.0.0.1:6379 128 | # root 17540 26376 0 15:15 pts/0 00:00:00 grep --color=auto redis 129 | 130 | # 删除临时文件 131 | rm -rf /usr/local/mydata/temp/redis-6.2.7 132 | ``` 133 | 134 | 启动成功之后,通过 `redis-cli` 命令进入到控制台,然后通过 `ping` 命令进行连通性测试,如果看到 `pong` ,表示连接成功了,如下: 135 | 136 | ```bash 137 | /usr/local/mydata/soft/redis/bin/redis-cli 138 | 139 | # 127.0.0.1:6379> ping 140 | # PONG 141 | # 127.0.0.1:6379> exit 142 | 143 | # 注意:如果在 `redis.conf` 配置文件配置了密码 `requirepass 123456` ,则可用 `redis-cli` 连接之后再用 `auth 123456` 完成认证(推荐),或者通过 `redis-cli -a 123456` 直接连接。 144 | ``` 145 | 146 | 另外,也可以使用可视化工具来连接 Redis ,比如:`Redis Desktop Manager` 。 147 | 148 | ## 5 关闭 149 | 150 | 通过 `shutdown` 命令关闭 `Redis` ,如下: 151 | 152 | ```bash 153 | /usr/local/mydata/soft/redis/bin/redis-cli 154 | 155 | # 127.0.0.1:6379> shutdown 156 | # not connected> exit 157 | ``` 158 | 159 | --- 160 | 161 | - [Redis 教程合集](https://mp.weixin.qq.com/s/iivXrj1cfTiPy89ueE_53Q)(微信左下方**阅读全文**可直达)。 162 | 163 | 164 | --- 165 | 166 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 167 | 168 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/powerjob.md: -------------------------------------------------------------------------------- 1 | PowerJob 是全新一代**分布式任务调度与计算框架**,能让您轻松完成作业的调度与繁杂任务的分布式计算。 2 | 3 | 4 | ## 1 简介 5 | 6 | ### 1.1 主要特性 7 | 8 | * 使用简单:提供前端 Web 界面,允许开发者可视化地完成调度任务的管理(增、删、改、查)、任务运行状态监控和运行日志查看等功能。 9 | * 定时策略完善:支持 CRON 表达式、固定频率、固定延迟和 API 四种定时调度策略。 10 | * 执行模式丰富:支持单机、广播、Map、MapReduce 四种执行模式,其中 Map/MapReduce 处理器能使开发者寥寥数行代码便获得集群分布式计算的能力。 11 | * **DAG 工作流支持**:支持在线配置任务依赖关系,可视化得对任务进行编排,同时还支持上下游任务间的数据传递。 12 | * 执行器支持广泛:支持 Spring Bean、内置/外置 Java 类、Shell、Python 等处理器,应用范围广。 13 | * 运维便捷:支持**在线日志**功能,执行器产生的日志可以在前端控制台页面实时显示,降低 debug 成本,极大地提高开发效率。 14 | * 依赖精简:最小仅依赖关系型数据库(MySQL/Oracle/MS SQLServer...),扩展依赖为 MongoDB (用于存储庞大的在线日志)。 15 | * 高可用&高性能:调度服务器经过精心设计,一改其他调度框架基于数据库锁的策略,实现了无锁化调度。部署多个调度服务器可以同时实现高可用和性能的提升(支持无限的水平扩展)。 16 | * **故障转移与恢复**:任务执行失败后,可根据配置的重试策略完成重试,只要执行器集群有足够的计算节点,任务就能顺利完成。 17 | 18 | ### 1.2 适用场景 19 | 20 | * 有定时执行需求的业务场景:如每天凌晨全量同步数据、生成业务报表等。 21 | * 有需要全部机器一同执行的业务场景:如使用广播执行模式清理集群日志。 22 | * 有需要分布式处理的业务场景:比如需要更新一大批数据,单机执行耗时非常长,可以使用 Map/MapReduce 处理器完成任务的分发,调动整个集群加速计算。 23 | * 有需要延迟执行某些任务的业务场景:比如订单过期处理等。 24 | 25 | ### 1.3 设计目标 26 | 27 | PowerJob 的设计目标为**企业级的分布式任务调度平台**,即成为公司内部的**任务调度中间件**。整个公司统一部署调度中心 `powerjob-server`,旗下所有业务线应用只需要依赖 `powerjob-worker` 即可接入调度中心获取任务调度与分布式计算能力。 28 | 29 | ### 1.4 在线试用 30 | 31 | - 试用地址:[http://192.168.71.53:7700/](http://192.168.71.53:7700/) 32 | - 应用名称:powerjob-worker-samples 33 | - 密码:123 34 | 35 | ### 1.5 同类产品对比 36 | 37 | ||QuartZ|xxl-job|SchedulerX 2.0|**PowerJob**| 38 | |:-|:-|:-|:-|:-| 39 | |定时类型|CRON|CRON|CRON、固定频率、固定延迟、OpenAPI|**CRON、固定频率、固定延迟、OpenAPI**| 40 | |任务类型|内置 Java|内置 Java、GLUE Java、Shell、Python 等脚本|内置 Java、外置 Java(FatJar)、Shell、Python 等脚本|**内置 Java、外置 Java(容器)、Shell、Python 等脚本**| 41 | |分布式任务|无|静态分片|MapReduce 动态分片|**MapReduce 动态分片**| 42 | |在线任务治理|不支持|支持|支持|**支持**| 43 | |日志白屏化|不支持|支持|不支持|**支持**| 44 | |调度方式及性能|基于数据库锁,有性能瓶颈|基于数据库锁,有性能瓶颈|不详|**无锁化设计,性能强劲无上限**| 45 | |报警监控|无|邮件|短信|**邮件和钉钉(另外开发者可基于接口方便扩展,如:短信)**| 46 | |系统依赖|JDBC 支持的关系型数据库(MySQL、Oracle...)|MySQL|人民币|**任意 Spring Data Jpa 支持的关系型数据库(MySQL、Oracle...)** | 47 | |DAG 工作流|不支持|不支持|支持|**支持**| 48 | 49 | ## 2 基本概念 50 | 51 | ### 2.1 分组概念 52 | 53 | - appName:应用名称,建议与用户实际接入调度中心的应用名称保持一致,**用于业务分组与隔离,一个 appName 等于一个业务集群,也就是实际的一个 Java 项目**。 54 | 55 | ### 2.2 核心概念 56 | 57 | - **任务(Job)**:描述了需要被调度中心调度的任务信息,包括任务名称、调度时间、处理器信息等。 58 | - **任务实例(JobInstance,简称 Instance)**:任务(Job)被调度执行后会生成任务实例(Instance),任务实例记录了任务的运行时信息(任务与任务实例的关系类似于类与对象的关系)。 59 | - **作业(Task)**:任务实例的执行单元,一个 JobInstance 存在至少一个 Task,具体规则如下: 60 | - 单机任务(STANDALONE):一个 JobInstance 对应一个 Task。 61 | - 广播任务(BROADCAST):一个 JobInstance 对应 N 个 Task,N 为集群机器数量,即每一台机器都会生成一个 Task。 62 | - Map/MapReduce 任务:一个 JobInstance 对应若干个 Task,由开发者手动 map 产生。 63 | - **工作流(Workflow)**:由 DAG(有向无环图)描述的一组任务(Job),用于任务编排。 64 | - **工作流实例(WorkflowInstance)**:工作流被调度执行后会生成工作流实例,记录了工作流的运行时信息。 65 | 66 | ### 2.3 扩展概念 67 | 68 | - 容器:以 Maven 工程项目的维度组织一堆 Java 文件(开发者开发的众多 Java 处理器),**可以通过前端网页动态发布并被执行器加载,具有极强的扩展能力和灵活性**。 69 | - OpenAPI:允许开发者通过接口来完成手工的操作,让系统整体变得更加灵活。开发者可以基于 API 便捷地扩展 PowerJob 原有的功能。 70 | 71 | ### 2.4 定时任务类型 72 | 73 | - API:该任务只会由 powerjob-client 中提供的 OpenAPI 接口触发,Server 不会主动调度。 74 | - CRON:该任务的调度时间由 CRON 表达式指定。 75 | - 固定频率:秒级任务,每隔多少毫秒运行一次,功能与 java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate 相同。 76 | - 固定延迟:秒级任务,延迟多少毫秒运行一次,功能与 java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay 相同。 77 | - 工作流:该任务只会由其所属的工作流调度执行,Server 不会主动调度该任务。如果该任务不属于任何一个工作流,该任务就不会被调度。 78 | 79 | > 备注:固定延迟和固定频率任务统称秒级任务,这两种任务无法被停止,**只有任务被关闭或删除时才能真正停止任务**。 80 | 81 | ## 3 项目地址 82 | 83 | - PowerJob 主项目:https://github.com/KFCFans/PowerJob 84 | - PowerJob 前端项目:https://github.com/PowerJob/PowerJob-Console 85 | - PowerJob 官网项目:https://github.com/PowerJob/Official-Website 86 | 87 | ## 4 项目结构说明 88 | 89 | 本项目由主体项目(`PowerJob`)和前端项目(`PowerJob-Console`)构成,其中 PowerJob 各模块说明如下: 90 | 91 | ``` 92 | ├── LICENSE 93 | ├── powerjob-client // powerjob-client,普通 Jar 包,提供 OpenAPI 94 | ├── powerjob-common // 各组件的公共依赖,开发者无需感知 95 | ├── powerjob-server // powerjob-server,基于 SpringBoot 实现的调度服务器 96 | ├── powerjob-worker // powerjob-worker, 普通 Jar 包,接入 powerjob-server 的应用需要依赖该 Jar 包 97 | ├── powerjob-worker-agent // powerjob-agent,可执行 Jar 文件,可直接接入 powerjob-server 的代理应用 98 | ├── powerjob-worker-samples // 教程项目,包含了各种 Java 处理器的编写样例 99 | ├── others 100 | └── pom.xml 101 | ``` 102 | 103 | ## 5 交流与讨论 104 | 105 | - QQ 群: 106 | - 微信群: 107 | 108 | --- 109 | 110 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 111 | 112 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-cors.md: -------------------------------------------------------------------------------- 1 | 学习在 Spring Boot 中通过 CORS 解决跨域问题。 2 | 3 | 4 | ## 1 介绍 5 | 6 | 先来了解下同源策略,它是由 Netscape 提出的一个著名的安全策略,是浏览器最核心,也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略,**同源是指协议、域名以及端口要相同**。传统的跨域解决方案是 JSONP , JSONP 虽然能解决跨域但是有一个很大的局限性,那就是只支持 GET 请求,不支持其他类型的请求。而 CORS ( Cross-origin resource sharing 跨域源资源共享)是一个 W3C 标准,它是一份浏览器技术的规范,提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略。 7 | 8 | ## 2 实战 9 | 10 | 新建 Spring Boot 工程 spring-boot-corsprovider ,用来提供服务,默认 8080 端口。新增测试类 HelloController ,如下: 11 | 12 | ```java 13 | @RestController 14 | public class HelloController { 15 | @GetMapping("/get") 16 | public String get() { 17 | return "get"; 18 | } 19 | 20 | @PutMapping("/put") 21 | public String put() { 22 | return "put"; 23 | } 24 | } 25 | ``` 26 | 27 | 新建 Spring Boot 工程 spring-boot-corsconsumer ,用来消费服务,配置 8081 端口。在 resources/static 下新建测试页面 index.html ,如下: 28 | 29 | ```html 30 | 31 | 32 | 33 | 34 | Title 35 | 36 | 37 | 38 |
39 | 40 | 41 | 58 | 59 | 60 | ``` 61 | 62 | 启动项目,访问 http://120.0.0.1:8081/index.html ,点击按钮后观察浏览器控制台,报跨域错误,如下: 63 | 64 | ``` 65 | Access to XMLHttpRequest at 'http://127.0.0.1:8080/get' from origin 'http://127.0.0.1:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 66 | ``` 67 | 68 | 接着修改 HelloController ,在类或方法上增加 CORS 的配置,如下: 69 | 70 | ```java 71 | @RestController 72 | // @CrossOrigin(origins = "http://127.0.0.1:8081") 73 | public class HelloController { 74 | @GetMapping("/get") 75 | @CrossOrigin(origins = "http://127.0.0.1:8081") 76 | public String get() { 77 | return "get"; 78 | } 79 | 80 | @PutMapping("/put") 81 | @CrossOrigin(origins = "http://127.0.0.1:8081") 82 | public String put() { 83 | return "put"; 84 | } 85 | } 86 | ``` 87 | 88 | 重启 spring-boot-corsprovider 再次测试,可以正常获取到数据。观察对应请求,发现 Response Headers 中多了 `Access-Control-Allow-Origin: http://127.0.0.1:8081` 表示服务端愿意接收来自 http://127.0.0.1:8081 的请求,这样浏览器就不会再去限制这个请求了。 89 | 90 | 上述CORS 的配置实在类或方法上,此外在 Spring Boot 中也支持全局配置,增加 `MyWebMvcConfigurer` 配置类,重写 addCorsMappings 方法,如下: 91 | 92 | ```java 93 | @Configuration 94 | public class MyWebMvcConfigurer implements WebMvcConfigurer { 95 | @Override 96 | public void addCorsMappings(CorsRegistry registry) { 97 | registry.addMapping("/**") 98 | .allowedOrigins("http://127.0.0.1:8081") 99 | .allowedHeaders("*") 100 | .allowedMethods("*") 101 | .maxAge(30 * 1000); 102 | } 103 | } 104 | ``` 105 | 106 | ## 3 风险 107 | 108 | 跨域问题虽然解决了,但带来了潜在的威胁,如:CSRF(Cross-site request forgery)跨站请求伪造。跨站请求伪造也被称为 one-click attack 或者 session riding ,通常缩写为 CSRF 或者 XSRF ,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意操作的攻击方法,如: 109 | 110 | > 假如一家银行用以运行转账操作的URL地址如下:`http://icbc.com/aa?bb=cc` ,那么,一个恶意攻击者可以在另一个网站上放置如下代码:``,如果用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会遭受损失。 111 | 112 | 基于此,浏览器在实际操作中,会对请求进行分类,分为简单请求,预先请求,带凭证的请求等,预先请求会首先发送一个 options 探测请求,和浏览器进行协商是否接受请求。默认情况下跨域请求是不需要凭证的,但是服务端可以配置要求客户端提供凭证,这样就可以有效避免 csrf 攻击。 113 | 114 | --- 115 | 116 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 117 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 118 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-web/spring-boot-cors](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-web/spring-boot-cors) 119 | 120 | 121 | --- 122 | 123 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 124 | 125 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/hr/hr-rabbitmq-reliability-consumer.md: -------------------------------------------------------------------------------- 1 | 学习如何在人力资源管理系统中提高 RabbitMQ 消息消费的可靠性,避免消息被重复消费。 2 | 3 | 4 | ## 1 概述 5 | 6 | 在 [如何在人力资源管理系统中提高 RabbitMQ 消息发送的可靠性](https://mp.weixin.qq.com/s/w3GiA7QhGLI6T-yeSHwfLQ) 一文中,我们确保了消息发送的可靠性。但是,在这样的机制下,又带来了新的问题,就是消息可能会重复投递,进而导致消息重复消费。例如一个员工入职了,结果收到了两封入职欢迎邮件。我们需要通过技术手段去处理这个问题。比如增加**消息消费确认机制**,可以有效的提高消息消费的可靠性。 7 | 8 | 说到这个话题,我们就不得不先来说说消息幂等性。 9 | 10 | ## 2 幂等性 11 | 12 | 幂等性本身是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。在开发领域,则表示对于同一个系统,使用相同的条件,一次请求和多次请求对系统资源的影响是一致的。 13 | 14 | 在分布式系统中幂等性尤为重要,因为分布式系统中,我们经常会用到接口调用失败进而进行重试这个功能,这样就带来了对一个接口可能会使用相同的条件进行重复调用,在这样的条件下,保证接口的幂等性就尤为重要了。常见的解决方案有: 15 | 16 | - MVCC 机制: MVCC 多版本并发控制,这种方式就是在数据更新的时候需要去比较所持有的数据版本号,版本号不一致的话,操作会失败,这样每个 version 就只有一次执行成功的机会,一旦失败了必须重新获取。 17 | - **Token 机制**: Token 则是目前使用比较广的一种方式,核心思想就是每个操作都有一个唯一凭证 token ,一旦执行成功,对于重复的请求,总是返回同一个结果。 18 | - 设计去重表 19 | - ... 20 | 21 | 人力资源管理系统中的 RabbitMQ 消费端实际上就是采用了 Token 这种方式。 22 | 23 | ## 3 Token 机制 24 | 25 | 大致的思路是这样,首先将 RabbitMQ 的消息自动确认机制改为手动确认,然后每当有一条消息消费成功了,就把该消息的唯一 ID 记录在 Redis 上,然后每次收到消息时,都先去 Redis 上查看是否有该消息的 ID,如果有,表示该消息已经消费过了,不再处理,否则再去处理。 26 | 27 | 首先,修改 `hr-mail` 中的 `pom` 文件,增加 Redis 依赖,如下: 28 | 29 | ```xml 30 | 31 | org.springframework.boot 32 | spring-boot-starter-data-redis 33 | 34 | ``` 35 | 36 | 接着,修改 `application.properties` 文件,增加 Redis 配置,并修改 RabbitMQ 配置,开启消息消费手动确认,如下: 37 | 38 | ```properties 39 | # 配置 RabbitMQ 40 | spring.rabbitmq.host=127.0.0.1 41 | spring.rabbitmq.port=5672 42 | spring.rabbitmq.username=guest 43 | spring.rabbitmq.password=guest 44 | ## 开启消息消费手动确认,默认自动 45 | spring.rabbitmq.listener.simple.acknowledge-mode=manual 46 | spring.rabbitmq.listener.simple.prefetch=100 47 | 48 | # 配置 Redis ,实现 RabbitMQ 消息消费的幂等性,避免消息重复消费,见 RabbitMQReceiver 49 | spring.redis.host=127.0.0.1 50 | spring.redis.port=6379 51 | spring.redis.database=0 52 | spring.redis.password= 53 | ``` 54 | 55 | --- 56 | 57 | ![](https://oscimg.oschina.net/oscnet/up-e6638b74275db538c4caf1b5875278f5e88.png) 58 | 59 | 最后,修改 `RabbitMQReceiver` 中的消息消费方法,增加**消息消费确认机制**,如下: 60 | 61 | ```java 62 | // 监听队列 63 | @RabbitListener(queues = MailConstants.QUEUE_NAME) 64 | public void employeeWelcome(Message message, Channel channel) throws IOException { 65 | Employee employee = (Employee) message.getPayload(); 66 | MessageHeaders headers = message.getHeaders(); 67 | Long tag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); 68 | String msgId = (String) headers.get("spring_returned_message_correlation"); 69 | if (stringRedisTemplate.opsForHash().entries("mail_log").containsKey(msgId)) { 70 | // redis 中包含该 key,说明该消息已经被消费过 71 | logger.info("消息已经被消费:" + msgId); 72 | channel.basicAck(tag, false); // 手动确认消息已消费 73 | return; 74 | } 75 | logger.info(employee.toString()); 76 | 77 | // 发送邮件 78 | MimeMessage msg = javaMailSender.createMimeMessage(); 79 | MimeMessageHelper helper = new MimeMessageHelper(msg); 80 | try { 81 | helper.setSubject("入职通知"); 82 | helper.setFrom(mailProperties.getUsername()); 83 | helper.setTo(employee.getEmail()); 84 | helper.setSentDate(new Date()); 85 | 86 | Context context = new Context(); 87 | context.setVariable("name", employee.getName()); 88 | context.setVariable("positionName", employee.getPosition().getName()); 89 | context.setVariable("jobTitlelName", employee.getJobTitle().getName()); 90 | context.setVariable("departmentName", employee.getDepartment().getName()); 91 | String process = templateEngine.process("employee/welcome", context); 92 | helper.setText(process, true); 93 | javaMailSender.send(msg); 94 | 95 | stringRedisTemplate.opsForHash().put("mail_log", msgId, "cxy35"); 96 | channel.basicAck(tag, false); // 手动确认消息已消费 97 | logger.info("邮件发送成功:" + msgId); 98 | } catch (MessagingException e) { 99 | channel.basicNack(tag, false, true); // 消息消费失败,重回队列 100 | e.printStackTrace(); 101 | logger.error("邮件发送失败:" + e.getMessage()); 102 | } 103 | } 104 | ``` 105 | 106 | --- 107 | 108 | - [Spring Boot 实战项目(人力资源管理系统)教程合集](https://mp.weixin.qq.com/s/2m9if4Skd2LR6vNezccwqw)(微信左下方**阅读全文**可直达)。 109 | - Spring Boot 实战项目(人力资源管理系统)源码:[https://github.com/cxy35/hr](https://github.com/cxy35/hr) 110 | 111 | 112 | --- 113 | 114 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 115 | 116 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/tool/docker-compose.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | 3 | 通过前面几篇文章的学习,我们可以通过 `Dockerfile` 文件让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况,或者开发一个 `Web` 应用,除了 `Web` 服务容器本身,还需要数据库服务容器、缓存容器,甚至还包括负载均衡容器等等。 4 | 5 | `Docker Compose` 恰好满足了这样的需求,它是用于定义和运行多容器 `Docker` 应用程序的工具。通过 `Compose`,您可以使用 `YAML` 文件来配置应用程序所需要的服务。然后使用一个命令,就可以通过 `YAML` 配置文件创建并启动所有服务。 6 | 7 | `Docker Compose` 项目是 `Docker` 官方的开源项目,来源于之前的 `Fig` 项目,使用 `Python` 语言编写。负责实现对 `Docker` 容器集群的快速编排。项目地址为:[https://github.com/docker/compose/releases](https://github.com/docker/compose/releases) 8 | 9 | `Docker Compose` 使用的三个步骤为: 10 | 11 | - 使用 `Dockerfile` 文件定义应用程序的环境; 12 | - 使用 `docker-compose.yml` 文件定义构成应用程序的服务,这样它们可以在隔离环境中一起运行; 13 | - 执行 `docker-compose up` 命令来创建并启动所有服务。 14 | 15 | ## 安装 16 | 17 | 官方文档:[https://docs.docker.com/compose/install/](https://docs.docker.com/compose/install/) 18 | 19 | ```bash 20 | # 下载二进制文件来使用 21 | curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 22 | # 因为 Docker Compose 存放在 GitHub,可能不太稳定。可以通过 DaoCloud 加速下载 23 | # curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose 24 | 25 | # 授权 26 | # 将可执行权限应用于该二进制文件 27 | sudo chmod +x /usr/local/bin/docker-compose 28 | 29 | # 测试 30 | docker-compose --version 31 | 32 | # 卸载 33 | rm -f /usr/local/bin/docker-compose 34 | ``` 35 | 36 | ## docker-compose.yml 文件详解 37 | 38 | 官方文档:[https://docs.docker.com/compose/compose-file/](https://docs.docker.com/compose/compose-file/) 39 | 40 | `Docker Compose` 允许用户通过 `docker-compose.yml` 文件(`YAML` 格式)来定义一组相关联的容器为一个工程(`project`)。一个工程包含多个服务(`service`),每个服务中定义了创建容器(`container`)时所需的镜像、参数、依赖等。 41 | 42 | `Docker Compose` 模板文件我们需要关注的顶级配置有 `version、services、networks、volumes` 几个部分,除 `version` 外,其他几个顶级配置下还有很多下级配置,后面也会详细给大家介绍,先来看看这几个顶级配置都什么意思: 43 | 44 | `version`:描述 `Compose` 文件的版本信息,当前最新版本为 `3.8`,对应的Docker版本为 `19.03.0+` 45 | `services`:定义服务,可以多个,每个服务中定义了创建容器时所需的镜像、参数、依赖等 46 | `networkds`:定义网络,可以多个,根据 `DNS server` 让相同网络中的容器可以直接通过容器名称进行通信 47 | `volumes`:数据卷,用于实现目录挂载 48 | 49 | 示例: 50 | 51 | ```yml 52 | # 描述 Compose 文件的版本信息 53 | version: "3.8" 54 | 55 | # 定义服务,可以多个 56 | services: 57 | mysql: # 服务名称,容器与容器之间可以用服务名称为域名进行访问,比如在 zmall-admin 服务中可以通过 jdbc:mysql//mysql:3306 这个地址来访问 mysql 这个服务。 58 | image: mysql:5.7 # 指定运行的镜像名称 59 | container_name: mysql # 配置容器名称,默认为"工程名称_服务条目名称_序号" 60 | ports: # 指定宿主机和容器的端口映射(HOST:CONTAINER) 61 | - "3306:3306" 62 | volumes: # 将宿主机的文件或目录挂载到容器中(HOST:CONTAINER) 63 | - /home/mysql/conf:/etc/mysql/conf.d 64 | - /home/mysql/data:/var/lib/mysql 65 | - /home/mysql/log:/var/log/mysql 66 | environment: # 配置环境变量 67 | - MYSQL_ROOT_PASSWORD=123456 68 | networks: # 非必须,配置容器连接的网络,引用顶级 networks 下的条目 69 | - mysql-net 70 | zmall-admin: # 服务2名称 71 | image: zmall/zmall-admin:1.0 72 | container_name: zmall-admin 73 | ports: 74 | - 8080:8080 75 | volumes: 76 | - /etc/localtime:/etc/localtime 77 | - /home/zmall/zmall-admin/logs:/var/logs 78 | environment: 79 | - 'TZ="Asia/Shanghai"' 80 | external_links: 81 | - mysql:db # 可以用 db 这个域名访问 mysql 服务 82 | # 定义网络,可以多个。如果不声明,默认会创建一个网络名称为"工程名称_default"的 bridge 网络 83 | networks: 84 | mysql-net: # 一个具体网络的条目名称 85 | name: mysql-net # 网络名称,默认为"工程名称_网络条目名称" 86 | driver: bridge # 网络模式,默认为 bridge 87 | ``` 88 | 89 | ## 常用命令 90 | 91 | 官方文档:[https://docs.docker.com/compose/reference/overview/](https://docs.docker.com/compose/reference/overview/) 92 | 93 | `docker-compose [-f ...] [options] [COMMAND] [ARGS...]` 94 | 95 | 部分命令选项如下: 96 | 97 | `-f,--file`:指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定,指定多个 yml; 98 | `-p, --project-name`:指定工程名称,默认使用 docker-compose.yml 文件所在目录的名称; 99 | `-v`:打印版本并退出; 100 | `--log-level`:定义日志等级(DEBUG, INFO, WARNING, ERROR, CRITICAL)。 101 | 102 | ### 查看容器 103 | 104 | ```bash 105 | # 列出工程中所有服务的容器 106 | docker-compose ps 107 | # 列出工程中指定服务的容器 108 | docker-compose ps mysql 109 | ``` 110 | 111 | ### 启动与停止容器 112 | 113 | ```bash 114 | # 前台启动 115 | docker-compose up 116 | # 后台启动 117 | docker-compose up -d 118 | # -f 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定,指定多个 yml 119 | docker-compose -f docker-compose.yml up -d 120 | 121 | # 停止工程中所有服务的容器 122 | docker-compose stop 123 | # 停止工程中指定服务的容器 124 | docker-compose stop mysql 125 | ``` 126 | 127 | 128 | --- 129 | 130 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 131 | 132 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/mq/rabbitmq-install-linux.md: -------------------------------------------------------------------------------- 1 | 手把手带你在 Linux 上安装 `RabbitMQ` 。 2 | 3 | 4 | ## 1 准备工作 5 | 6 | ### 1.1 依赖软件和版本说明 7 | 8 | - `RabbitMQ` 需要 `Erlang` 和 `Socat` 环境,`RabbitMQ` 版本和 `Erlang` 版本兼容性关系可参考 [https://www.rabbitmq.com/which-erlang.html](https://www.rabbitmq.com/which-erlang.html) 。 9 | - **特别注意操作系统版本**,可以执行 `cat /etc/centos-release` 查看,`CentOS 6.x` 对应 `el6` 的安装包版本,`CentOS 7.x` 对应 `el7` 的安装包版本,`CentOS 8.x` 对应 `el8` 的安装包版本。 10 | 11 | ### 1.2 下载安装包 12 | 13 | - 访问 [https://github.com/rabbitmq/erlang-rpm/releases](https://github.com/rabbitmq/erlang-rpm/releases) 下载 `Erlang` 对应**稳定版**的安装包,如:`erlang-23.3.4.11-1.el7.x86_64.rpm`。 14 | - 访问 [http://www.rpmfind.net/linux/rpm2html/search.php?query=socat(x86-64)](http://www.rpmfind.net/linux/rpm2html/search.php?query=socat(x86-64)) 下载 `Socat` 对应**稳定版**的安装包,如:`socat-1.7.4.3-2.3.x86_64.rpm`。 15 | - 访问 [https://github.com/rabbitmq/rabbitmq-server/releases](https://github.com/rabbitmq/rabbitmq-server/releases) 下载 `RabbitMQ` 对应**稳定版**的安装包,如:`rabbitmq-server-3.9.16-1.el7.noarch.rpm`。 16 | - 上传到服务器的 `/usr/local/mydata/temp` 目录下。如果没有,则手动新建。 17 | 18 | ### 1.3 安装相关依赖环境 19 | 20 | ```bash 21 | yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz 22 | ``` 23 | 24 | ## 2 安装 25 | 26 | ### 2.1 安装 Erlang 27 | 28 | ```bash 29 | # 安装,其中 erlang-23.3.4.11-1.el7.x86_64.rpm 换成实际的名称 30 | cd /usr/local/mydata/temp 31 | rpm -ivh erlang-23.3.4.11-1.el7.x86_64.rpm 32 | 33 | # 验证 34 | erl -v 35 | # Erlang/OTP 23 [erts-11.2.2.10] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] 36 | # 37 | # Eshell V11.2.2.10 (abort with ^G) 38 | 39 | rpm -qa|grep -i erlang 40 | # erlang-23.3.4.11-1.el7.x86_64 41 | ``` 42 | 43 | ### 2.2 安装 Socat 44 | 45 | ```bash 46 | # 安装,其中 socat-1.7.3.2-2.el7.x86_64.rpm 换成实际的名称 47 | cd /usr/local/mydata/temp 48 | rpm -ivh socat-1.7.3.2-2.el7.x86_64.rpm 49 | 50 | # 验证 51 | rpm -qa|grep -i socat 52 | # socat-1.7.3.2-2.el7.x86_64 53 | ``` 54 | 55 | > 如果报 `错误:依赖检测失败` 安装对应的依赖之后,重新安装即可。 56 | 57 | ### 2.3 安装 RabbitMQ 58 | 59 | ```bash 60 | # 安装,其中 rabbitmq-server-3.9.16-1.el7.noarch.rpm 换成实际的名称 61 | cd /usr/local/mydata/temp 62 | rpm -ivh rabbitmq-server-3.9.16-1.el7.noarch.rpm 63 | 64 | # 验证 65 | rpm -qa|grep -i rabbitmq 66 | # rabbitmq-server-3.9.16-1.el7.noarch 67 | ``` 68 | 69 | ## 3 配置 70 | 71 | ### 3.1 开启管理界面 72 | 73 | 执行:`rabbitmq-plugins enable rabbitmq_management` 开启管理界面。 74 | 75 | ### 3.2 用户相关操作 76 | 77 | `RabbitMQ` 默认的用户 `guest/guest` 只能在本机访问,所以需要新增一个远程登录的用户。 78 | 79 | 执行:`systemctl start rabbitmq-server` 启动服务。 80 | 81 | ```bash 82 | # 新增用户 83 | rabbitmqctl add_user admin 123456 84 | 85 | # 设置用户分配操作权限 86 | rabbitmqctl set_user_tags admin administrator 87 | # administrator:可以登录控制台、查看所有信息、可以对 rabbitmq 进行管理 88 | # monitoring:监控者 登录控制台,查看所有信息 89 | # policymaker:策略制定者 登录控制台,指定策略 90 | # managment:普通管理员 登录控制台 91 | 92 | # 为用户添加资源权限 93 | rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" 94 | 95 | # --------------- 96 | 97 | # 修改用户密码 98 | rabbitmqctl change_password admin 654321 99 | # 查询用户列表 100 | rabbitmqctl list_users 101 | # 删除用户 102 | rabbitmqctl delete_user admin 103 | ``` 104 | 105 | ## 4 启动停止重启 106 | 107 | ```bash 108 | # 启动服务 109 | systemctl start rabbitmq-server 110 | # 停止服务 111 | systemctl stop rabbitmq-server 112 | # 重启服务 113 | systemctl restart rabbitmq-server 114 | # 查看服务状态 115 | systemctl status rabbitmq-server 116 | # 开机自启动 117 | systemctl enable rabbitmq-server 118 | ``` 119 | 120 | 启动成功后,访问 [http://127.0.0.1:15672/](http://127.0.0.1:15672/),可以看到登录页面,如果无法访问,排查下防火墙端口是否开放。 121 | 122 | ## 5 卸载 123 | 124 | ```bash 125 | # 停止服务 126 | systemctl stop rabbitmq-server 127 | # 查看安装的服务 128 | rpm -qa|grep -i rabbitmq 129 | # rabbitmq-server-3.9.16-1.el7.noarch 130 | # 卸载,名称以实际为准 131 | rpm -e rabbitmq-server-3.9.16-1.el7.noarch --nodeps 132 | 133 | # 查看安装的服务 134 | rpm -qa|grep -i erlang 135 | # erlang-23.3.4.11-1.el7.x86_64 136 | # 卸载,名称以实际为准 137 | rpm -e erlang-23.3.4.11-1.el7.x86_64 --nodeps 138 | 139 | 140 | # 查看安装的服务 141 | rpm -qa|grep -i socat 142 | # socat-1.7.3.2-2.el7.x86_64 143 | # 卸载,名称以实际为准 144 | rpm -e socat-1.7.3.2-2.el7.x86_64 --nodeps 145 | 146 | # 查询并删除相关文件,名称和目录以实际为准 147 | find / -name rabbitmq 148 | find / -name erlang 149 | find / -name socat 150 | rm -rf /run/rabbitmq /etc/rabbitmq /var/lib/rabbitmq /var/log/rabbitmq /usr/lib/rabbitmq /usr/lib64/erlang /usr/share/java/erlang /usr/bin/socat 151 | ``` 152 | 153 | 154 | --- 155 | 156 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 157 | 158 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springcloud/spring-cloud-overview.md: -------------------------------------------------------------------------------- 1 | 通过本文学习**微服务介绍**、 **Spring Cloud 介绍**,让大家对 Spring Cloud 有个初步的认识。 2 | 3 | 4 | ## 1 微服务介绍 5 | 6 | 2009 年, `Netflix` 公司重新定义了它的应用程序员的开发模型,这个算是微服务的首次探索。 7 | 8 | 2014 年, [《Microservices》](https://www.martinfowler.com/articles/microservices.html) 这篇文章以一个更加通俗易懂的方式,为大家定义了微服务。 9 | 10 | **互联网应用**产品的两大特点: 11 | 12 | - 需求变化快。 13 | - 用户群体庞大。 14 | 15 | 在这样的情况下,我们需要构建一个能够**灵活扩展**,同时能够**快速应对外部环境变化**的一个应用,使用传统的开发方式,显然无法满足需求。这个时候,微服务就登场了。 16 | 17 | ### 1.1 什么是微服务 18 | 19 | 简单来说,微服务就是一种**将一个单一应用程序拆分为一组小型服务**的方法,拆分完成后,每一个服务都运行在独立的进程中,**服务与服务之间采用轻量级的通信机制来进行沟通**(其中 Spring Cloud 中采用基于 HTTP 的 RESTful API )。 20 | 21 | 每一个服务,都是围绕具体的业务进行构建,例如一个电商系统,包括:订单服务、支付服务、物流服务、会员服务等,这些拆分后的应用都是独立的应用,都可以独立的部署到生产环境中。同时在采用微服务之后,我们的项目不再拘泥于一种语言,可以是 Java/Go/Python/PHP 等混合使用,这在传统的应用开发中,是无法想象的。而使用了微服务之后,我们可以根据业务上下文来选择合适的语言和构建工具进行构建。 22 | 23 | 微服务可以理解为是 `SOA` 的一个传承,一个本质的区别是微服务是一个真正分布式、去中心化的,微服务的拆分比 SOA 更加彻底。 24 | 25 | ### 1.2 微服务的优势 26 | 27 | - 复杂度可控。 28 | - 独立部署。 29 | - 技术选型灵活。 30 | - 较好的容错性。 31 | - 较强的可扩展性。 32 | 33 | ### 1.3 微服务的特性 34 | 35 | - 不主动 36 | - 不拒绝 37 | - 不负责 38 | 39 | ## 2 Spring Cloud 介绍 40 | 41 | Spring Cloud 可以理解为微服务这种思想在 Java 领域的一个具体落地。 Spring Cloud 在发展之初,就借鉴了微服务的思想,同时结合 Spring Boot 让 Spring Cloud 具备了组件的一键式启动和部署的能力,极大的简化了微服务架构的落地。 42 | 43 | Spring Cloud 这种框架,从设计之初,就充分考虑了分布式架构演化所需要的功能,例如服务注册、配置中心、消息总线、负载均衡等。这些功能都是以可插拔的形式提供出来的,这样,在分布式系统不断演化的过程中,我们的 Spring Cloud 也可以非常方便的进化。 44 | 45 | ### 2.1 什么是 Spring Cloud 46 | 47 | Spring Cloud 是一系列框架的集合, 内部包含了许多框架,这些框架互相协作,共同来构建**分布式系统**。利用这些组件,可以非常方便的构建一个分布式系统。 48 | 49 | ### 2.2 核心特性 50 | 51 | - 分布式配置中心。 52 | - 服务注册与发现。 53 | - 链路器。 54 | - 服务调用。 55 | - 负载均衡。 56 | - 断路器/熔断机制。 57 | - 全局锁。 58 | - 选举与集群状态管理。 59 | - 分布式消息/消息总线。 60 | 61 | ### 2.3 版本名称 62 | 63 | 不同于其他的框架, Spring Cloud 的版本名称是通过 A(Angel)、 B(Brixton)、 C(Camden)、D(Dalston)、 E(Edgware)、 F(Finchley) 这样来命名的,这些名字使用了伦敦地铁站的名 64 | 字,目前最新版是 `Hoxton SR3` 。 65 | 66 | Spring Cloud 中,除了大的版本之外,还有一些小版本,小版本命名方式如下: 67 | 68 | - `M`: 是 Milestone 的缩写,如: M1/M2。 69 | - `RC`: 是 Release Candidate 的缩写,表示该项目处于候选状态,这是正式发版之前的一个状态,如: RC1/RC2 。 70 | - `SR`: 是 Service Release 的缩写,表示项目正式发布的稳定版,相当于 GA(Generally Available) 版。如: SR1/SR2 。 71 | - `SNAPSHOT`: 表示快照版。 72 | 73 | ### 2.4 组件 74 | 75 | - `Spring Cloud Netflix`: 这个组件,在 Spring Cloud 成立之初,立下了汗马功劳。但 2018 年的断更事件,使得 Netflix 走下神坛。 76 | - `Spring Cloud Config`: 分布式配置中心,利用 Git/SVN 来集中管理项目的配置文件。 77 | - `Spring Cloud Bus`: 消息总线,可以构建消息驱动的微服务,也可以用来做一些状态管理等。 78 | - `Spring Cloud Consul`: 服务注册与发现。 79 | - `Spring Cloud Stream`: 基于 Redis/RabbitMQ/Kafka 实现的消息微服务。 80 | - `Spring Cloud OpenFeign`: 提供 OpenFeign 集成到 Spring Boot 应用中的方式,主要解决微服务之间的调用问题。 81 | - `Spring Cloud Gateway`: Spring Cloud 官方推出的网关服务。 82 | - `Spring Cloud Cloudfoundry`: 利用 Cloudfoundry 集成我们的应用程序。 83 | - `Spring Cloud Security`: 在 Zuul 代理中,为 OAuth2 客户端认证提供支持。 84 | - `Spring Cloud AWS`: 快速集成亚马逊云服务。 85 | - `Spring Cloud Contract`: 一个消费者驱动的、面向 Java 的契约框架。 86 | - `Spring Cloud Zookeeper`: 基于 Apache Zookeeper 的服务注册和发现。 87 | - `Spring Cloud Data Flow`: 在一个结构化的平台上,组成数据微服务。 88 | - `Spring Cloud Kubernetes`: Spring Cloud 提供的针对 Kubernetes 的支持。 89 | - `Spring Cloud Function`: 90 | - `Spring Cloud Task`: 短生命周期的微服务。 91 | 92 | ||Spring Cloud 第一代|Spring Cloud 第二代| 93 | |:-|:-|:-| 94 | |网关|spring-cloud-netflix-zuul(来源于 Netflix Zuul ,性能一般)|Spring Cloud Gateway| 95 | |注册中心|spring-cloud-netflix-eureka(集成于 Netflix Eureka ,不再维护,Consul,ZK)|[阿里 Nacos](https://github.com/alibaba/nacos) ,拍拍贷 radar 等可选| 96 | |配置中心|spring-cloud-config(自研,功能不足,国内使用其它配置中心替代)|[阿里 Nacos](https://github.com/alibaba/nacos) ,[携程 Apollo](https://github.com/ctripcorp/apollo) ,[随行付 Config Keeper](https://github.com/sxfad/config-keeper)| 97 | |客户端软负载均衡|spring-cloud-ribbon(来源于 Netflix 集成,不支持 webFlux 的负载均衡)|[spring-cloud-loadbalancer](https://github.com/spring-cloud-incubator/spring-cloud-loadbalancer)| 98 | |熔断器|spring-cloud-netflix-hystrix(来源于 Netflix 集成,不再开发新功能,进入维护状态)|[spring-cloud-r4j(Resilience4J)](https://github.com/spring-cloud-incubator/spring-cloud-r4j),[阿里 Sentinel](https://github.com/alibaba/Sentinel)| 99 | 100 | ### 2.5 与 Spring Boot 的版本对应关系 101 | 102 | |Spring Cloud 版本|Spring Boot 版本| 103 | |:-|:-| 104 | |`Hoxton`|2.2.x| 105 | |`Greenwich`|2.1.x| 106 | |`Finchley`|2.0.x| 107 | |`Edgware`|1.5.x| 108 | |`Dalston`|1.5.x| 109 | 110 | --- 111 | 112 | - [Spring Cloud 教程合集](https://mp.weixin.qq.com/s/SBmcs2bxumhNz4kky1pl-A)(微信左下方**阅读全文**可直达)。 113 | - Spring Cloud 教程合集示例代码:[https://github.com/cxy35/spring-cloud-samples](https://github.com/cxy35/spring-cloud-samples) 114 | 115 | 116 | --- 117 | 118 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 119 | 120 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/other/mat.md: -------------------------------------------------------------------------------- 1 | `MAT(Memory Analyzer Tools)`是一个快速且功能丰富的 Java 堆分析器,可帮助您查找内存泄漏并减少内存消耗。使用 MAT 分析具有数亿个对象的高效堆转储,快速计算对象的保留大小,查看谁阻止垃圾收集器收集对象,运行报告以自动提取泄漏嫌疑者。 2 | 3 | 4 | ## 1 简介 5 | 6 | MAT 是一款非常强大的内存分析工具,在 Eclipse 中有相应的插件,同时也有单独的安装包。在进行内存分析时,只要获得了反映当前设备内存映像的 `hprof` 文件,通过 MAT 打开就可以直观地看到当前的内存信息。 7 | 8 | ## 2 使用 9 | 10 | ### 2.1 准备 MAT 11 | 12 | 下载独立版本的 MAT,下载地址:[https://www.eclipse.org/mat/downloads.php](https://www.eclipse.org/mat/downloads.php),下载后解压。找到 `MemoryAnalyzer.ini` 文件,该文件里面有个 Xmx 参数,该参数表示最大内存占用量,默认为 1024m,根据堆转储文件大小修改该参数即可。 13 | 14 | ### 2.2 准备堆转储文件(Heap Dump) 15 | 16 | 堆转储文件(Heap Dump)是 Java 进程在某个时间内的快照(.hprof 格式)。它在触发快照的时候保存了很多信息,如:Java 对象和类信息(通常在写堆转储文件前会触发一次 Full GC)。 17 | 18 | 堆转储文件信息: 19 | 20 | ![](https://oscimg.oschina.net/oscnet/up-db8fee5cc58791f2c51aa1fbce366809f9c.png) 21 | 22 | - 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。 23 | - 所有的类信息,包括 classloader、类名称、父类、静态变量等。 24 | - GC Root 到所有的这些对象的引用路径。 25 | - 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)。 26 | 27 | 多种方式获取堆转储文件: 28 | 29 | - **通过 jmap 命令可以在 cmd 里执行:`jmap -dump:format=b,file=<文件名.hprof> `。** 30 | - 如果想在发生内存溢出的时候自动 dump,需要添加下面 JVM 参数:`-XX:+HeapDumpOnOutOfMemoryError`。 31 | - 使用 Ctrl+Break 组合键主动获取获取,需要添加下面 JVM 参数:`-XX:+HeapDumpOnCtrlBreak`。 32 | - 使用 HPROF Agent 可以在程序执行结束时或受到 SIGOUT 信号时生成 Dump 文件,配置在虚拟机的参数如下:`-agentlib:hprof=heap=dump,format=b`。 33 | - 使用 JConsole 获取。 34 | - 使用 Memory Analyzer Tools 的 `File -> Acquire Heap Dump` 功能获取。 35 | 36 | ### 2.3 分析堆转储文件 37 | 38 | 打开 MAT 之后,加载 dump 文件,差不多就下面这样的界面: 39 | 40 | ![](https://oscimg.oschina.net/oscnet/up-eb9612daab3df6538a49f68574d01622030.png) 41 | 42 | 常用的两个功能:`Histogram、 Leak Suspects`。 43 | 44 | #### 2.3.1 Histogram 45 | 46 | ![](https://oscimg.oschina.net/oscnet/up-a3801c1d8338c96a2fe01061e507e9f7e80.png) 47 | 48 | Histogram 可以列出内存中的对象,对象的个数及其内存大小,可以用来定位哪些对象在 Full GC 之后还活着,哪些对象占大部分内存。 49 | 50 | - Class Name:类名称,Java 类名。 51 | - Objects:类的对象的数量,这个对象被创建了多少个。 52 | - Shallow Heap:对象本身占用内存的大小,不包含其引用的对象内存,实际分析中作用不大。常规对象(非数组)的 Shallow Size 由其成员变量的数量和类型决定。数组的 Shallow Size 由数组元素的类型(对象类型、基本类型)和数组长度决定。对象成员都是些引用,真正的内存都在堆上,看起来是一堆原生的 byte[], char[], int[],对象本身的内存都很小。 53 | - Retained Heap:计算方式是将 Retained Set(当该对象被回收时那些将被 GC 回收的对象集合)中的所有对象大小叠加。或者说,因为 X 被释放,导致其它所有被释放对象(包括被递归释放的)所占的 heap 大小。Retained Heap 可以更精确的反映一个对象实际占用的大小。 54 | 55 | > Retained Heap 例子:一个 ArrayList 对象持有 100 个对象,每一个占用 16 bytes,如果这个 list 对象被回收,那么其中 100 个对象也可以被回收,可以回收 16*100 + X 的内存,X 代表 ArrayList 的 shallow 大小。 56 | 57 | --- 58 | 59 | ![](https://oscimg.oschina.net/oscnet/up-d7b74ba04668526df9babf40c65adf2e166.png) 60 | 61 | 在上述列表中选择一个 Class,右键选择 `List objects > with incoming references`,在新页面会显示通过这个 class 创建的对象信息。 62 | 63 | --- 64 | 65 | ![](https://oscimg.oschina.net/oscnet/up-d86206a873731d7be3b035c43b36c5c9ec7.png) 66 | 67 | 继续选择一个对象,右键选择 `Path to GC Roots > ****` ,通常在排查**内存泄漏(一般是因为存在无效的引用)**的时候,我们会选择 `exclude all phantom/weak/soft etc.references`,意思是查看排除虚引用/弱引用/软引用等的引用链,因为被虚引用/弱引用/软引用的对象可以直接被 GC 给回收,我们要看的就是某个对象否还存在 Strong 引用链(在导出 Heap Dump 之前要手动触发 GC 来保证),如果有,则说明存在内存泄漏,然后再去排查具体引用。 68 | 69 | 这时会**拿到 GC Roots 到该对象的路径**,通过对象之间的引用,可以清楚的看出这个对象没有被回收的原因,然后再去定位问题。如果上面对象此时本来应该是被 GC 掉的,简单的办法就是将其中的某处置为 null 或者 remove 掉,使其到 GC Root 无路径可达,处于不可触及状态,垃圾回收器就可以回收了。**反之,一个存在 GC Root 的对象是不会被垃圾回收器回收掉的。** 70 | 71 | #### 2.3.2 Leak Suspects 72 | 73 | ![](https://oscimg.oschina.net/oscnet/up-15737d76ad546ebe13cbf645d9bfe7ba2d4.png) 74 | 75 | Leak Suspects 可以自动分析并提示可能存在的内存泄漏,可以直接定位到 Class 及对应的行数。 76 | 77 | --- 78 | 79 | ![](https://oscimg.oschina.net/oscnet/up-c18534c14b6de80b23f9c1035249cfaea39.png) 80 | 81 | 比如:这里问题一的描述,列出了一些比较大的实例。点击 `Details` 可以看到细节信息,另外还可点击 `See stacktrace` 查看具体的线程栈信息(可直接定位到具体某个类中的方法)。 82 | 83 | ![](https://oscimg.oschina.net/oscnet/up-5b7138318eb4b3b4011a890a279e22f56b0.png) 84 | 85 | 在 Details 详情页面 `Shortest Paths To the Accumulation Point` 表示 GC root 到内存消耗聚集点的最短路径,如果某个内存消耗聚集点有路径到达 GC root,则该内存消耗聚集点不会被当做垃圾被回收。 86 | 87 | > 实战:在某项目中,其中几个 Tomcat 响应特别慢,打开 `Java VisualVM` 观察 `Tomcat(pid xxx)-Visual GC` 发现 `Spaces-Old` 升高,`Graphs-GC Time` 比较频繁且持续时间长、有尖峰(重启后过段时间又出现了),最后通过 `Leak Suspects` 中的 `See stacktrace` 定位到某个查询接口,仔细排查代码后发现有个 BUG:在特定查询条件下会一次性查询几万的数据出来(因为脏数据),处理过后恢复正常。 88 | 89 | ![](https://oscimg.oschina.net/oscnet/up-572f17f3da93b7310f4a5e6d6cf094876f0.JPEG) 90 | 91 | #### 2.3.3 内存快照对比 92 | 93 | 为了更有效率的找出内存泄露的对象,一般会获取两个堆转储文件(先 dump 一个,隔段时间再 dump 一个),通过对比后的结果可以很方便定位。 94 | 95 | ![](https://oscimg.oschina.net/oscnet/up-c6c514ac8a4c3d27583adb2ddc46862c9fc.png) 96 | 97 | ![](https://oscimg.oschina.net/oscnet/up-9d52c6e0ca7ee7cd7283063497a79eb6805.png) 98 | 99 | 100 | --- 101 | 102 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 103 | 104 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) -------------------------------------------------------------------------------- /docs/springboot/spring-boot-springsecurity-user.md: -------------------------------------------------------------------------------- 1 | Spring Boot 整合 Spring Security 之后,默认用户名为 user ,密码在项目启动时打印在控制台。这个随机生成的密码,每次项目启动时都会变,不是很方便。我们可以自己配置 Spring Security 的用户和角色,有三种方式可以实现: 2 | 3 | - 通过 `application.properties` 配置文件配置**在内存中**。 4 | - 通过 Java 代码配置**在内存中**。 5 | - 配置**在数据库中**,然后通过 Java 代码从数据库中加载。 6 | 7 | 本文学习前面两种**在内存中**的配置方式,**在数据库中**的配置方式可以参考 [Spring Boot 整合 Spring Security(配置用户/角色-基于数据库)]() 。 8 | 9 | 10 | ## 1 创建工程 11 | 12 | 创建 Spring Boot 项目 `spring-boot-springsecurity-user` ,添加 `Web/Spring Security` 依赖,如下: 13 | 14 | ![](https://oscimg.oschina.net/oscnet/up-1d23871e606c43a843a073470a40bc2081d.png) 15 | 16 | 最终的依赖如下: 17 | 18 | ```xml 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-security 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | org.junit.vintage 36 | junit-vintage-engine 37 | 38 | 39 | 40 | 41 | org.springframework.security 42 | spring-security-test 43 | test 44 | 45 | 46 | ``` 47 | 48 | ## 2 配置用户和角色 49 | 50 | 1. 通过 `application.properties` 配置文件。 51 | 52 | ```properties 53 | # 方法1:通过配置文件配置用户/角色 54 | # spring.security.user.name=admin 55 | # spring.security.user.password=123456 56 | # spring.security.user.roles=admin 57 | ``` 58 | 59 | 2. 通过 Java 代码。 60 | 61 | 新增 `SecurityConfig` 配置类,如下: 62 | 63 | ```java 64 | // 方法2:通过 SecurityConfig 配置用户和角色 65 | @Configuration 66 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 67 | @Bean 68 | PasswordEncoder passwordEncoder() { 69 | // return NoOpPasswordEncoder.getInstance();// 密码不加密 70 | return new BCryptPasswordEncoder();// 密码加密 71 | } 72 | 73 | @Override 74 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 75 | // 在内存中配置2个用户 76 | /*auth.inMemoryAuthentication() 77 | .withUser("admin").password("123456").roles("admin") 78 | .and() 79 | .withUser("user").password("123456").roles("user");// 密码不加密*/ 80 | 81 | auth.inMemoryAuthentication() 82 | .withUser("admin").password("$2a$10$fB2UU8iJmXsjpdk6T6hGMup8uNcJnOGwo2.QGR.e3qjIsdPYaS4LO").roles("admin") 83 | .and() 84 | .withUser("user").password("$2a$10$3TQ2HO/Xz1bVHw5nlfYTBON2TDJsQ0FMDwAS81uh7D.i9ax5DR46q").roles("user");// 密码加密 85 | } 86 | } 87 | ``` 88 | 89 | 配置类说明: 90 | 91 | - 在 configure 方法中配置了两个用户,密码是加密之后的字符串(明文为 123456)。 92 | - 从 Spring 5 开始,强制要求密码要加密。如果坚持不想加密,可以使用一个过期的 `PasswordEncoder` 的实例 `NoOpPasswordEncoder` ,但不建议。 93 | - Spring Security 中提供了 `BCryptPasswordEncoder` 密码编码工具,可以非常方便的实现密码的加密加盐,相同明文加密出来的结果总是不同,这样就不需要用户去额外保存 `盐` 的字段了,这一点比 Shiro 要方便很多。 94 | 95 | ```java 96 | @SpringBootTest 97 | class SpringBootSpringsecurityUserApplicationTests { 98 | @Test 99 | void contextLoads() { 100 | // 同样的明文加密后不重复 101 | for (int i = 0; i < 10; i++) { 102 | BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); 103 | System.out.println(encoder.encode("123456")); 104 | } 105 | } 106 | } 107 | ``` 108 | 109 | ## 3 测试 110 | 111 | 新增测试类 `HelloController` ,如下: 112 | 113 | ```java 114 | @RestController 115 | public class HelloController { 116 | @GetMapping("/hello") 117 | public String hello() { 118 | return "hello"; 119 | } 120 | } 121 | ``` 122 | 123 | 项目启动之后,浏览器访问 [http://127.0.0.1:8080/hello](http://127.0.0.1:8080/hello) ,跳转到登录页面,用上述配置的用户名和密码就能登录了。 124 | 125 | --- 126 | 127 | - [Spring Boot 教程合集](https://mp.weixin.qq.com/s/9vOiAxHFnfJnRwSlTfAHwg)(微信左下方**阅读全文**可直达)。 128 | - Spring Boot 教程合集示例代码:[https://github.com/cxy35/spring-boot-samples](https://github.com/cxy35/spring-boot-samples) 129 | - 本文示例代码:[https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-security/spring-boot-springsecurity-user](https://github.com/cxy35/spring-boot-samples/tree/master/spring-boot-security/spring-boot-springsecurity-user) 130 | 131 | 132 | --- 133 | 134 | 扫码关注微信公众号 **程序员35** ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点:[https://cxy35.top](https://cxy35.top) 135 | 136 | ![](https://oscimg.oschina.net/oscnet/up-285838b9c516db5bb1ba760f292f2346078.JPEG) --------------------------------------------------------------------------------