1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/qwls-springboot_shiro_redis

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
db_mblog.sql 190 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
远方的码农 Отправлено 23.05.2019 06:24 d692d8b
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
-- MySQL dump 10.13 Distrib 5.7.26, for linux-glibc2.12 (x86_64)
--
-- Host: localhost Database: db_mblog
-- ------------------------------------------------------
-- Server version 5.7.26
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `bl_attach`
--
DROP TABLE IF EXISTS `bl_attach`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_attach` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(100) DEFAULT NULL,
`ftype` varchar(50) DEFAULT NULL,
`fkey` varchar(100) DEFAULT NULL,
`author_id` int(11) DEFAULT NULL,
`created` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_attach`
--
LOCK TABLES `bl_attach` WRITE;
/*!40000 ALTER TABLE `bl_attach` DISABLE KEYS */;
INSERT INTO `bl_attach` VALUES (10,'a4.jpg','image','article/2018/02/7lt38sn21ci3qobfpach5r9cm7.jpg',3,1519809459);
/*!40000 ALTER TABLE `bl_attach` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_comments`
--
DROP TABLE IF EXISTS `bl_comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_comments` (
`coid` int(11) NOT NULL AUTO_INCREMENT,
`cid` int(11) DEFAULT '0',
`created` int(11) DEFAULT NULL,
`author` varchar(200) DEFAULT NULL,
`author_id` int(11) DEFAULT '0',
`owner_id` int(11) DEFAULT '0',
`mail` varchar(200) DEFAULT NULL,
`url` varchar(200) DEFAULT NULL,
`ip` varchar(100) DEFAULT NULL,
`agent` varchar(200) DEFAULT NULL,
`content` longtext,
`type` varchar(50) DEFAULT NULL,
`status` varchar(50) DEFAULT NULL,
`parent` int(11) DEFAULT '0',
PRIMARY KEY (`coid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_comments`
--
LOCK TABLES `bl_comments` WRITE;
/*!40000 ALTER TABLE `bl_comments` DISABLE KEYS */;
/*!40000 ALTER TABLE `bl_comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_contents`
--
DROP TABLE IF EXISTS `bl_contents`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_contents` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(500) DEFAULT NULL,
`slug` varchar(500) DEFAULT NULL,
`thumb_img` varchar(500) DEFAULT NULL,
`created` int(11) DEFAULT NULL,
`modified` int(11) DEFAULT NULL,
`content` longtext,
`author_id` int(11) DEFAULT NULL,
`type` varchar(50) DEFAULT NULL,
`status` varchar(50) DEFAULT NULL,
`fmt_type` varchar(50) DEFAULT 'markdown',
`tags` varchar(200) DEFAULT NULL,
`categories` varchar(200) DEFAULT NULL,
`hits` int(11) DEFAULT '0',
`comments_num` int(11) DEFAULT '0',
`allow_comment` int(11) DEFAULT NULL,
`allow_ping` int(11) DEFAULT NULL,
`allow_feed` int(11) DEFAULT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_contents`
--
LOCK TABLES `bl_contents` WRITE;
/*!40000 ALTER TABLE `bl_contents` DISABLE KEYS */;
INSERT INTO `bl_contents` VALUES (1,'关于','about',NULL,1514428648,1516342230,'### 目的\r\n 开通本网站目的很简单。\r\n\r\n主要是熟悉整个流程,以及linux的学习。\r\n\r\n另外就是方便整理一些笔记,属实是学习到了很多东西。\r\n\r\n在这条道路上走着,还是给自己留一点东西下来吧~\r\n\r\n:wink::wink::wink::wink:\r\n### 介绍\r\n主修Java\r\n\r\n90后,双子座,中度强迫症吧~~~\r\n\r\n音乐:纯音 电音 偏欧美 日系 粤语\r\n\r\n电影:偏科幻 搞笑\r\n\r\n目前经常出没地上海,老家在遥远的四川遂宁\r\n\r\n### 网站\r\n本网站主语言Java~~\r\n\r\n使用了Spring Boot开发,网页主题采用了漂亮的 Pinghsu\r\n\r\n数据库是比较常规的MySql\r\n',1,'page','publish','markdown','','',399,0,1,1,1),(2,'网站使用技术及更新记录',NULL,NULL,1514428648,1517623763,'## 前言\r\n本项目修改自开源项目Tale,\r\n\r\n## 使用相关\r\n为了表示感谢原作者以及以后的学习这里放点链接\r\n\r\n- <p><a href=\'https://gitee.com/biezhi/tale\' target=\'_blank\'>Tale</a> 博客系统</p>\r\n- <p><a href=\'https://github.com/chakhsu/pinghsu\' target=\'_blank\'>Pinghsu</a> 主题</p>\r\n- <p><a href=\'https://github.com/lets-blade/blade\' target=\'_blank\'>Blade</a> 轻量级mvc框架</p>\r\n- <p><a href=\'https://github.com/vdurmont/emoji-java\' target=\'_blank\'>Emoji-Java</a> 轻量级的java库</p>\r\n- <p><a href=\'https://github.com/subchen/jetbrick-template-2x\' target=\'_blank\'>Jetbrick-Template</a> 模板引擎</p>\r\n\r\n## 网站更新\r\n\r\n- 添加了独立搜索页\r\n- 修改原系统数据库Sqllite为MySql\r\n## 变迁历史\r\n- 于2017年11月上线\r\n- 于2017年12月买了第一个域名www.yangxs.ink\r\n\r\n## 服务器\r\n- 阿里云 CenterOS\r\n- 域名也是阿里云的',1,'post','publish','markdown','博客,日志','Daily',377,0,1,1,1),(3,'友情链接','links',NULL,1514428648,1515165967,'## 友情链接\r\n\r\n- :lock: <a href=\'http://www.begincode.net\' target=\'blank\'>Java技术论坛</a>\r\n\r\n## 链接须知\r\n> 请确定贵站可以稳定运营\r\n> 原创博客优先,技术类博客优先,设计、视觉类博客优先\r\n> 经常过来访问和评论,眼熟的\r\n\r\n备注:默认申请友情链接均为内页(当前页面)\r\n\r\n## 基本信息\r\n\r\n 网站名称:yangxs博客\r\n 网站地址:www.yangxs.ink\r\n请在当页通过评论来申请友链,其他地方不予回复\r\n\r\n暂时先这样,同时欢迎互换友链,这个页面留言即可。 ^_^\r\n\r\n还有,我会不定时对无法访问的网址进行清理,请保证自己的链接长期有效。',1,'page','publish','markdown','','',416,0,1,1,NULL),(5,'最近听的歌曲',NULL,NULL,1514431277,1516349439,'### 分享一些歌曲:\r\n---\r\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=509313150&auto=0&height=66\"></iframe>\r\n\r\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=436514415&auto=0&height=66\"></iframe>\r\n\r\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"//music.163.com/outchain/player?type=2&id=515143305&auto=0&height=66\"></iframe>',1,'post','publish','markdown','音乐','Music',309,0,1,1,1);
/*!40000 ALTER TABLE `bl_contents` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_metas`
--
DROP TABLE IF EXISTS `bl_metas`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_metas` (
`mid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) DEFAULT NULL,
`slug` varchar(200) DEFAULT NULL,
`type` varchar(32) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`sort` int(11) DEFAULT '0',
`parent` int(11) DEFAULT '0',
PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_metas`
--
LOCK TABLES `bl_metas` WRITE;
/*!40000 ALTER TABLE `bl_metas` DISABLE KEYS */;
INSERT INTO `bl_metas` VALUES (4,'音乐','音乐','tag',NULL,0,0),(16,'博客','博客','tag',NULL,0,0),(17,'日志','日志','tag',NULL,0,0);
/*!40000 ALTER TABLE `bl_metas` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_options`
--
DROP TABLE IF EXISTS `bl_options`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_options` (
`name` varchar(100) NOT NULL,
`value` longtext NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_options`
--
LOCK TABLES `bl_options` WRITE;
/*!40000 ALTER TABLE `bl_options` DISABLE KEYS */;
INSERT INTO `bl_options` VALUES ('allow_install','1','是否允许重新安装博客'),('site_description','',NULL),('site_keywords','',NULL),('site_theme','default',NULL),('site_title','qw_blog',NULL),('site_url','http://localhost:8080/install',NULL),('social_github','yangxsa',NULL),('social_twitter','yangxs3',NULL),('social_weibo','',NULL),('social_zhihu','',NULL);
/*!40000 ALTER TABLE `bl_options` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_relationships`
--
DROP TABLE IF EXISTS `bl_relationships`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_relationships` (
`cid` int(11) NOT NULL,
`mid` int(11) NOT NULL,
PRIMARY KEY (`cid`,`mid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_relationships`
--
LOCK TABLES `bl_relationships` WRITE;
/*!40000 ALTER TABLE `bl_relationships` DISABLE KEYS */;
INSERT INTO `bl_relationships` VALUES (2,14),(2,16),(2,17),(5,4),(5,14),(5,15),(7,6),(7,13),(8,9),(8,10),(8,13),(10,10),(10,12),(10,18),(11,12),(11,19),(11,20),(12,12),(12,20),(12,21);
/*!40000 ALTER TABLE `bl_relationships` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `bl_users`
--
DROP TABLE IF EXISTS `bl_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `bl_users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`home_url` varchar(255) DEFAULT NULL,
`screen_name` varchar(100) DEFAULT NULL,
`created` int(11) DEFAULT NULL,
`activated` int(11) DEFAULT NULL,
`logged` int(11) DEFAULT NULL,
`group_name` varchar(16) DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY `AK_UNQ_BL_USER_USERNAME` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `bl_users`
--
LOCK TABLES `bl_users` WRITE;
/*!40000 ALTER TABLE `bl_users` DISABLE KEYS */;
INSERT INTO `bl_users` VALUES (4,'qinwei','2ca28294d1294a78918573ff12ae0141','1547325335@qq.com',NULL,NULL,NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `bl_users` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `flyway_schema_history`
--
DROP TABLE IF EXISTS `flyway_schema_history`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `flyway_schema_history` (
`installed_rank` int(11) NOT NULL,
`version` varchar(50) DEFAULT NULL,
`description` varchar(200) NOT NULL,
`type` varchar(20) NOT NULL,
`script` varchar(1000) NOT NULL,
`checksum` int(11) DEFAULT NULL,
`installed_by` varchar(100) NOT NULL,
`installed_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`execution_time` int(11) NOT NULL,
`success` tinyint(1) NOT NULL,
PRIMARY KEY (`installed_rank`),
KEY `flyway_schema_history_s_idx` (`success`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `flyway_schema_history`
--
LOCK TABLES `flyway_schema_history` WRITE;
/*!40000 ALTER TABLE `flyway_schema_history` DISABLE KEYS */;
INSERT INTO `flyway_schema_history` VALUES (1,'3.2','update','SQL','V3.2__update.sql',-53734780,'root','2019-03-26 01:15:05',20,1);
/*!40000 ALTER TABLE `flyway_schema_history` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_channel`
--
DROP TABLE IF EXISTS `mto_channel`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_channel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key_` varchar(32) DEFAULT NULL,
`name` varchar(32) DEFAULT NULL,
`status` int(5) NOT NULL,
`thumbnail` varchar(128) DEFAULT NULL,
`weight` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_channel`
--
LOCK TABLES `mto_channel` WRITE;
/*!40000 ALTER TABLE `mto_channel` DISABLE KEYS */;
INSERT INTO `mto_channel` VALUES (1,'banner','banner',1,'',3),(2,'blog','博客',0,'',2),(3,'jotting','随笔',0,'',1),(4,'share','分享',0,'',0);
/*!40000 ALTER TABLE `mto_channel` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_comment`
--
DROP TABLE IF EXISTS `mto_comment`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_comment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`author_id` bigint(20) DEFAULT NULL,
`content` varchar(255) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`pid` bigint(20) NOT NULL,
`post_id` bigint(20) DEFAULT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `IK_POST_ID` (`post_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_comment`
--
LOCK TABLES `mto_comment` WRITE;
/*!40000 ALTER TABLE `mto_comment` DISABLE KEYS */;
INSERT INTO `mto_comment` VALUES (1,1,'文章写得太好了|&acute;・&omega;・)ノ(╯‵□&prime;)╯︵┴─┴','2019-05-14 17:31:04',0,2,0),(2,1,'你说楼主怎么想的这个','2019-05-14 17:31:22',1,2,0),(3,2,'hello','2019-05-14 17:35:05',0,2,0),(4,1,'6666','2019-05-22 17:41:23',0,5,0);
/*!40000 ALTER TABLE `mto_comment` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_favorite`
--
DROP TABLE IF EXISTS `mto_favorite`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_favorite` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created` datetime DEFAULT NULL,
`post_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IK_USER_ID` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_favorite`
--
LOCK TABLES `mto_favorite` WRITE;
/*!40000 ALTER TABLE `mto_favorite` DISABLE KEYS */;
INSERT INTO `mto_favorite` VALUES (1,'2019-05-17 16:20:49',2,1);
/*!40000 ALTER TABLE `mto_favorite` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_message`
--
DROP TABLE IF EXISTS `mto_message`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_message` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created` datetime DEFAULT NULL,
`event` int(11) NOT NULL,
`from_id` bigint(20) DEFAULT NULL,
`post_id` bigint(20) DEFAULT NULL,
`status` int(11) NOT NULL,
`user_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_message`
--
LOCK TABLES `mto_message` WRITE;
/*!40000 ALTER TABLE `mto_message` DISABLE KEYS */;
INSERT INTO `mto_message` VALUES (1,'2019-05-14 17:31:05',3,1,2,1,1),(2,'2019-05-14 17:31:22',4,1,2,1,1),(3,'2019-05-17 16:20:49',1,1,2,1,1),(4,'2019-05-22 17:41:24',3,1,5,1,1);
/*!40000 ALTER TABLE `mto_message` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_options`
--
DROP TABLE IF EXISTS `mto_options`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_options` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`key_` varchar(32) DEFAULT NULL,
`type` int(5) DEFAULT '0',
`value` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_options`
--
LOCK TABLES `mto_options` WRITE;
/*!40000 ALTER TABLE `mto_options` DISABLE KEYS */;
INSERT INTO `mto_options` VALUES (1,'site_name',0,'Mtons'),(2,'site_domain',0,'http://mtons.com'),(3,'site_keywords',0,'mtons,博客,社区'),(4,'site_description',0,'Mtons, 做一个有内涵的技术社区'),(5,'site_metas',0,''),(6,'site_copyright',0,'Copyright © Mtons'),(7,'site_icp',0,''),(8,'qq_callback',0,''),(9,'qq_app_id',0,''),(10,'qq_app_key',0,''),(11,'weibo_callback',0,''),(12,'weibo_client_id',0,''),(13,'weibo_client_sercret',0,''),(14,'github_callback',0,''),(15,'github_client_id',0,''),(16,'github_secret_key',0,'');
/*!40000 ALTER TABLE `mto_options` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_post`
--
DROP TABLE IF EXISTS `mto_post`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_post` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`author_id` bigint(20) DEFAULT NULL,
`channel_id` int(11) DEFAULT NULL,
`comments` int(11) NOT NULL,
`created` datetime DEFAULT NULL,
`favors` int(11) NOT NULL,
`featured` int(11) NOT NULL,
`status` int(11) NOT NULL,
`summary` varchar(140) DEFAULT NULL,
`tags` varchar(64) DEFAULT NULL,
`thumbnail` varchar(128) DEFAULT NULL,
`title` varchar(64) DEFAULT NULL,
`views` int(11) NOT NULL,
`weight` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `IK_CHANNEL_ID` (`channel_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_post`
--
LOCK TABLES `mto_post` WRITE;
/*!40000 ALTER TABLE `mto_post` DISABLE KEYS */;
INSERT INTO `mto_post` VALUES (2,1,2,2,'2019-05-14 17:29:22',1,0,0,'&lt;dependency&gt; &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt; &lt;artifactId&gt;httpasyncclient&lt;/artifactI...','','/storage/thumbnails/2019/0522/22171636takx.jpg',' java发送http请求时处理异步回调结果',22,2),(3,1,2,0,'2019-05-22 17:17:38',0,0,0,'当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以...','','/storage/thumbnails/2019/0522/22171734qvrw.jpg','干货!!!MySQL 大表优化方案',2,1),(4,1,3,0,'2019-05-22 17:18:43',0,0,0,'随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求。因此,Redis 基于内存存储数据,可以极大的提高查询性能,对产品在架构上很好的补充。在某些场景下,可以充分的利用 Redis 的特性,大大提高效率。 缓存对于热点数据,缓存以后可...','redis','/storage/thumbnails/2019/0522/22171829snuw.jpg','Redis 使用场景',0,0),(5,1,2,1,'2019-05-22 17:24:05',0,0,0,'分布式锁一般有三种实现方式:1.&nbsp;数据库乐观锁;2.&nbsp;基于Redis的分布式锁;3.&nbsp;基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布式锁实...','Redis,分布式','/storage/thumbnails/2019/0522/22172344gkxd.jpg','Redis分布式锁的正确实现方式',35,0),(6,1,2,0,'2019-05-22 17:25:42',0,0,0,'&lt;dependency&gt; &lt;groupId&gt;javax&lt;/groupId&gt; &lt;artifactId&gt;javaee-api&lt;/artifactId&gt; &lt;version&gt;7.0&...','springboot,事务','/storage/thumbnails/2019/0522/22172531ufwn.jpg','springboot动态添加定时任务',4,0),(7,1,4,0,'2019-05-23 09:40:30',0,0,0,'实参(argument): &nbsp;&nbsp;全称为\"实际参数\"是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用...','','/storage/thumbnails/2019/0523/23094039wsvt.jpg','形参和实参的区别',0,0),(8,1,2,0,'2019-05-23 09:41:27',0,0,0,'# 该程序实现刷CSDN网页访问量,当访问被拒绝或者遇到其他异常时会自动重启,无限刷 # 经过测试发现大概间隔70秒访问一下,访问量才会增加1 # 只需要修改或者添加url的链接就可以了 import urllib.request import ...','','/storage/thumbnails/2019/0523/23094125gmah.jpg','python爬取CSDN博客访问量',0,0),(9,1,2,0,'2019-05-23 09:42:15',0,0,0,'var xhr = new XMLHttpRequest(); xhr.open(\'POST\', url, true); //第三个参数决定是否采用异步的方式 xhr.send(data); xhr.onreadystatechange = fu...','','/storage/thumbnails/2019/0523/23094213dejj.jpg','异步回调之javascript',0,0),(10,1,2,0,'2019-05-23 09:45:38',0,0,0,'前言:最近小王同学又遇到了一个需求:线上的业务运行了一段时间,后来随着使用人数增多,出现了一个问题是这样的,一个订单会重复创建几次,导致数据库里出现了很多垃圾数据。在测试同学的不断测试下,发现问题出在了前端的一个提交按钮上,有的用户比较“着急”,...','','/storage/thumbnails/2019/0523/23094536bsut.jpg','java如何优雅的实现时间控制',0,0),(11,1,2,0,'2019-05-23 09:46:30',0,0,0,'需要在get方法上面加入&nbsp; @JsonFormat(locale=\"zh\", timezone=\"GMT+8\", pattern=\"yyyy-MM-dd HH:mm:ss\") &nbsp;这个注解就可以了','','/storage/thumbnails/2019/0523/23094624scqb.jpg','java中实体类实现时间日期自动转换',0,0),(12,1,2,0,'2019-05-23 09:49:54',0,0,0,'一、Maven依赖,我把所有的依赖贴出来了,大家可以自行删减 &lt;dependencies&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId...','session,springboot','/storage/thumbnails/2019/0523/23094943fhce.jpg','解决springboot实现跨域session共享问题',0,0),(13,1,2,0,'2019-05-23 09:52:11',0,0,0,'1.maven依赖 &lt;dependency&gt; &lt;groupId&gt;commons-io&lt;/groupId&gt; &lt;artifactId&gt;commons-io&lt;/artifactId&gt; &lt;...','跨域','/storage/thumbnails/2019/0523/23095228rpgm.jpg','springboot多文件跨域上传',1,0),(14,1,2,0,'2019-05-23 09:54:15',0,0,0,'&lt;dependency&gt; &lt;groupId&gt;com.auth0&lt;/groupId&gt; &lt;artifactId&gt;java-jwt&lt;/artifactId&gt; &lt;version&gt;2....','token','/storage/thumbnails/2019/0523/23095408rpav.jpg','前后端分离,获取token,验证登陆是否失效',0,0),(15,1,2,0,'2019-05-23 09:58:31',0,0,0,'安装ActiveMq 下载地址:http://activemq.apache.org/activemq-5140-release.html http://127.0.0.1:8161/admin/&nbsp;为访问地址:账户默认为:admin...','ActiveMq springboot','/storage/thumbnails/2019/0523/23095820pham.jpg','SpringBoot+ActiveMq实现点对点消息队列(一)',0,0),(16,1,2,0,'2019-05-23 09:59:33',0,1,0,'结合上一篇点对点(queue),本篇为Topic yml修改一个配置: spring: #默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置 jms: pub-sub-domain: true package...','','/storage/thumbnails/2019/0523/23095931xhog.jpg','ActiveMQ消息处理机制-Topic方式(二)',0,0),(17,1,2,0,'2019-05-23 10:07:59',0,1,0,'前言 在项目开发中,对于异常处理我们通常有多种处理方式,比如:控制层手动捕获异常,拦截器统一处理异常。今天跟大家分享一种注解的方式,统一拦截异常并处理。 异常处理 在spring 3.2中,新增了@RestControllerAdvice 注解,...','Email','/storage/thumbnails/2019/0523/23100748kwvk.jpg','SpringBoot开发案例之异常处理并邮件通知',0,0),(18,1,2,0,'2019-05-23 10:09:26',0,0,0,'前言 由于业务需要,需要在拦截器中操作Redis缓存,按照 controller,service层配置发现无法注入,一直报空指针异常。 解决方案 @Configuration public class MyWebAppConfigurer ext...','','/storage/thumbnails/2019/0523/23100923ibxx.jpg','SpringBoot开发案例之拦截器注入Bean',0,0),(19,1,2,0,'2019-05-23 10:12:44',0,0,0,'前言 Nginx日志默认情况下写入到一个文件中,为了区分各个域下的日志,我们一般会分开存储。即时这样,文件也会变的越来越大,非常不方便查看分析。通常我们是以每日来做统计的,下面来聊聊以日期来分隔Nginx日志。 配置 编写脚本 #!/bin/ba...','Nginx','/storage/thumbnails/2019/0523/23101240yeri.jpg','Nginx学习之定时切割日志',0,0),(20,1,2,0,'2019-05-23 10:13:40',0,0,0,'前言 最近半年因为项目清闲,做了很多自学计划。很多都是心血来潮,也并没有都坚持下来,比如学习C语言、学习Spring和web基础知识、学习操作系统等。 在这个过程中,突然意识到“心态”是学习中一个很重要的部分。程序员大部分是独自学习的,很容易进入...','','/storage/thumbnails/2019/0523/23101337jqjq.jpg','如何保持一个稳定的自学心态 | 程序员',0,5),(21,1,2,0,'2019-05-23 10:14:59',0,0,0,'前言 有时候我们可能会遇到git提交错误的情况,比如提交了敏感的信息或者提交了错误的版本,这个时候我们想将提交到代码库的记录删除。 获取要回滚到的提交点的hash值 首先,我们需要找到我们需要回滚到的提交点的hash,可以使用 $ git log...','git','/storage/thumbnails/2019/0523/23101453mzcf.jpg','Git提交错误时如何删除Git提交记录',0,0),(22,1,2,0,'2019-05-23 10:17:02',0,0,0,'前言 大家都知道现在很多站点下载资料都是要收费的,无论是积分还是金币,想免费只能说很少很少了,那么这些网站是如何做到资源防盗链的呢? 这里推荐一款比较容易上手的神器,Nginx本身提供了secure_link来完成防盗链功能,可以给服务器文件链接...','','/storage/thumbnails/2019/0523/23102016hiki.jpg','Nginx学习之如何搭建文件防盗链服务',1,4),(23,1,2,0,'2019-05-23 10:19:47',0,0,0,'前言 在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了。在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使用的 xml 配置方式。 改造前 之前在 Sp...','','/storage/thumbnails/2019/0523/23101945kscj.jpg','SpringBoot开发案例之整合Dubbo分布式服务',0,3);
/*!40000 ALTER TABLE `mto_post` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_post_attribute`
--
DROP TABLE IF EXISTS `mto_post_attribute`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_post_attribute` (
`id` bigint(20) NOT NULL,
`content` longtext,
`editor` varchar(16) DEFAULT 'tinymce',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_post_attribute`
--
LOCK TABLES `mto_post_attribute` WRITE;
/*!40000 ALTER TABLE `mto_post_attribute` DISABLE KEYS */;
INSERT INTO `mto_post_attribute` VALUES (2,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<pre class=\"language-java\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt;\r\n &lt;artifactId&gt;httpasyncclient&lt;/artifactId&gt;\r\n &lt;version&gt;4.1.1&lt;/version&gt;\r\n &lt;/dependency&gt;</code></pre>\r\n<p>1.线程类 负责处理业务</p>\r\n<pre class=\"language-java\"><code>package com.ruoyi.test;\r\n \r\nimport java.io.UnsupportedEncodingException;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/4/10\r\n * Time:10:28\r\n */\r\npublic class Business extends Thread{\r\n // 回答1+1,很简单的问题不需要线程\r\n public int add(int num1, int num2) {\r\n return num1 + num2;\r\n }\r\n \r\n // 重写run方法\r\n @Override\r\n public void run() {\r\n // 回答地球为什么是圆的\r\n askquestion();\r\n super.run();\r\n }\r\n \r\n // 回调接口的创建,里面要有一个回调方法\r\n //回调接口什么时候用呢?这个思路是最重要的\r\n //\r\n \r\n public static interface Calls {\r\n public void call(String question);\r\n }\r\n \r\n // 回调接口的对象\r\n Calls calls;\r\n \r\n // 回答地球为什么是圆的\r\n private void askquestion() {\r\n System.err.println(\"开始查找资料!\");\r\n try {\r\n // 业务请求处理\r\n String succes = Test.main();\r\n \r\n // 把答案返回到回调接口的call方法里面\r\n if (calls!=null) {//提问者实例化callPhone对象,相当于提问者已经告诉我,我到时用什么方式回复答案\r\n //这个接口的方法实现是在提问者的类里面\r\n calls.call(succes);\r\n }\r\n } catch (Exception e) {\r\n e.printStackTrace();\r\n }\r\n \r\n \r\n }\r\n}</code></pre>\r\n<p>2.请求接口类</p>\r\n<pre class=\"language-java\"><code>package com.ruoyi.test;\r\n \r\nimport com.ruoyi.common.json.JSONObject;\r\nimport org.apache.http.HttpEntity;\r\nimport org.apache.http.HttpResponse;\r\nimport org.apache.http.client.methods.HttpPost;\r\nimport org.apache.http.concurrent.FutureCallback;\r\nimport org.apache.http.entity.StringEntity;\r\nimport org.apache.http.impl.nio.client.CloseableHttpAsyncClient;\r\nimport org.apache.http.impl.nio.client.HttpAsyncClients;\r\nimport org.apache.http.util.EntityUtils;\r\n \r\nimport java.io.IOException;\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.util.concurrent.CountDownLatch;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/4/10\r\n * Time:9:15\r\n */\r\npublic class Test {\r\n \r\n public static String main() throws UnsupportedEncodingException {\r\n final String[] resData = new String[1];\r\n CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();\r\n client.start();\r\n final CountDownLatch latch = new CountDownLatch(1);\r\n final HttpPost post = new HttpPost(\"http://127.0.0.1:8088/login\");\r\n String param1=\"loginName=6210308024916652&amp;password=123456&amp;captcha=10\";\r\n JSONObject param2= new JSONObject();\r\n param2.put(\"loginName\", \"6210308024916652\");\r\n param2.put(\"password\", \"123456\");\r\n param2.put(\"captcha\", \"4\");\r\n //设置请求头 这里根据个人来定义\r\n post.addHeader(\"Content-type\", \"application/json; charset=utf-8\");\r\n post.setHeader(\"Accept\", \"application/json\");\r\n StringEntity stringEntity = new StringEntity(param2.toString());\r\n post.setEntity(stringEntity);\r\n //执行\r\n client.execute(post, new FutureCallback&lt;HttpResponse&gt;() {\r\n //执行异步操作 请求完成后\r\n @Override\r\n public void completed(final HttpResponse response) {\r\n latch.countDown();\r\n //响应内容\r\n int a = response.getStatusLine().getStatusCode();\r\n System.out.println(\"状态码:\"+a);\r\n if (a == 200) {\r\n HttpEntity entity = response.getEntity();\r\n try {\r\n resData[0] = EntityUtils.toString(entity);\r\n } catch (IOException e) {\r\n e.printStackTrace();\r\n }\r\n System.out.println(\"成功!\");\r\n } else {\r\n try {\r\n //输出响内容\r\n System.out.println(response.getStatusLine().getStatusCode()\r\n + \" \" + EntityUtils.toString(response.getEntity(), \"UTF-8\"));\r\n } catch (IOException e) {\r\n e.printStackTrace();\r\n }\r\n }\r\n }\r\n \r\n //请求失败处理\r\n @Override\r\n public void failed(final Exception ex) {\r\n latch.countDown();\r\n }\r\n \r\n //请求取消后处理\r\n @Override\r\n public void cancelled() {\r\n latch.countDown();\r\n }\r\n \r\n });\r\n \r\n try {\r\n latch.await();\r\n } catch (InterruptedException e) {\r\n e.printStackTrace();\r\n }\r\n //关闭\r\n try {\r\n client.close();\r\n } catch (IOException ignore) {\r\n \r\n }\r\n return resData[0];\r\n }\r\n}</code></pre>\r\n<p>3.测试类</p>\r\n<pre class=\"language-java\"><code>package com.ruoyi.test;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/4/10\r\n * Time:10:28\r\n */\r\npublic class MainClass {\r\n \r\n /**\r\n * java回调方法的使用\r\n * 实际操作时的步骤:(以本实例解释)\r\n * 1.在回答者的类内创建回调的接口\r\n * 2.在回答者的类内创建回调接口的对象,\r\n * 3.在提问者类里面实例化接口对象,重写接口方法\r\n * 2.-3.这个点很重要,回调对象的实例化,要在提问者的类内实例化,然后重写接口的方法\r\n * 相当于提问者先把一个联络方式给回答者,回答者找到答案后,通过固定的联络方式,来告诉提问者答案。\r\n * 4.调用开始新线程的start方法\r\n * 5.原来的提问者还可以做自己的事\r\n * */\r\n public static void main(String[] args) {\r\n // 小王问小张1+1=?,线程同步\r\n Business xiaoZhang = new Business();\r\n int i = xiaoZhang.add(1, 1);//回答1+1的答案\r\n \r\n // 问小张地球为什么是圆的?回调方法的使用\r\n //这相当于先定好一个返答案的方式,再来执行实际操作\r\n \r\n // 实例化回调接口的对象\r\n Business.Calls call = new Business.Calls() {\r\n @Override\r\n public void call(String question) {\r\n //回答问题者,回答后,才能输出答案\r\n System.err.println(question);\r\n }\r\n };\r\n \r\n //把回调对象赋值给回答者的回调对象,回答问题者的回调对象才能回答问题\r\n xiaoZhang.calls = call;\r\n \r\n System.out.println(\"吩咐完毕!\");\r\n //相关交代完毕之后再执行查询操作\r\n xiaoZhang.start();\r\n \r\n //小王做自己的事!\r\n System.out.println(\"处理自己的业务\");\r\n }\r\n}</code></pre>\r\n<p>4.请求结果</p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190410105654727.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n<p>总结:</p>\r\n<p>1.最好在服务端做一个sleep等待,这样可以更好的模拟效果</p>\r\n<p>2.开启一个子线程去执行请求不影响主线程运行,请求完毕后回调给用户</p>\r\n<p>3.还有很多实现异步回调方式,希望大家评论留言</p>\r\n<p>&nbsp;</p>\r\n<h3><strong>加群:687942640</strong></h3>\r\n</body>\r\n</html>','tinymce'),(3,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:</p>\r\n<p>单表优化<br />除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量:</p>\r\n<p>字段<br />1、尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED 2、VARCHAR的长度只分配真正需要的空间 3、使用枚举或整数代替字符串类型 4、尽量使用TIMESTAMP而非DATETIME, 5、单表不要有太多字段,建议在20以内 6、避免使用NULL字段,很难查询优化且占用额外索引空间 7、用整型来存IP</p>\r\n<p>索引<br />1、索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描 2、应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描 3、值分布很稀少的字段不适合建索引,例如&rdquo;性别&rdquo;这种只有两三个值的字段 4、字符字段只建前缀索引 5、字符字段最好不要做主键 6、不用外键,由程序保证约束 7、尽量不用UNIQUE,由程序保证约束 8、使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引</p>\r\n<p>查询SQL<br />1、可通过开启慢查询日志来找出较慢的SQL 2、不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边 3、sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库 4、不用SELECT * 5、OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内 6、不用函数和触发器,在应用程序实现 7、避免%xxx式查询 8、少用JOIN 9、使用同类型进行比较,比如用\'123\'\'123\'比,123和123比 10、尽量避免在WHERE子句中使用 != 或 &lt;&gt; 操作符,否则将引擎放弃使用索引而进行全表扫描 11、对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5 12、列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大</p>\r\n<p>引擎<br />目前广泛使用的是MyISAM和InnoDB两种引擎:</p>\r\n<p>MyISAM</p>\r\n<p>MyISAM引擎是MySQL 5.1及之前版本的默认引擎,它的特点是:</p>\r\n<p>1、不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁 2、不支持事务 3、不支持外键 4、不支持崩溃后的安全恢复 5、在表有读取查询的同时,支持往表中插入新纪录 6、支持BLOB和TEXT的前500个字符索引,支持全文索引 7、支持延迟更新索引,极大提升写入性能 8、对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用</p>\r\n<p>InnoDB</p>\r\n<p>InnoDB在MySQL 5.5后成为默认索引,它的特点是:</p>\r\n<p>1、支持行锁,采用MVCC来支持高并发 2、支持事务 3、支持外键 4、支持崩溃后的安全恢复 5、不支持全文索引</p>\r\n<p>总体来讲,MyISAM适合SELECT密集型的表,而InnoDB适合INSERT和UPDATE密集型的表</p>\r\n<p>系统调优参数<br />可以使用下面几个工具来做基准测试:</p>\r\n<p>sysbench:一个模块化,跨平台以及多线程的性能测试工具 iibench-mysql:基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具 tpcc-mysql:Percona开发的TPC-C测试工具</p>\r\n<p>具体的调优参数内容较多,具体可参考官方文档,这里介绍一些比较重要的参数:</p>\r\n<p>back_log</p>\r\n<p>backlog值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说,如果MySql的连接数据达到maxconnections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即backlog,如果等待连接的数量超过backlog,将不被授予连接资源。可以从默认的50升至500</p>\r\n<p>wait_timeout</p>\r\n<p>数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的8小时减到半小时</p>\r\n<p>maxuserconnection</p>\r\n<p>最大连接数,默认为0无上限,最好设一个合理上限thread_concurrency:并发线程数,设为CPU核数的两倍</p>\r\n<p>skipnameresolve</p>\r\n<p>禁止对外部连接进行DNS解析,消除DNS解析时间,但需要所有远程主机用IP访问</p>\r\n<p>keybuffersize</p>\r\n<p>索引块的缓存大小,增加会提升索引处理速度,对MyISAM表性能影响最大。对于内存4G左右,可设为256M或384M,通过查询show status like\'keyread%\',保证keyreads / keyreadrequests在0.1%以下最好</p>\r\n<p>innodbbufferpool_size</p>\r\n<p>缓存数据块和索引块,对InnoDB表性能影响最大。通过查询show status like \'Innodbbufferpoolread%\',保证 (Innodbbufferpoolreadrequests &ndash; Innodbbufferpoolreads)/ Innodbbufferpoolreadrequests 越高越好</p>\r\n<p>innodbadditionalmempoolsize</p>\r\n<p>InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL会记录Warning信息到数据库的错误日志中,这时就需要该调整这个参数大小</p>\r\n<p>innodblogbuffer_size</p>\r\n<p>InnoDB存储引擎的事务日志所使用的缓冲区,一般来说不建议超过32MB</p>\r\n<p>querycachesize</p>\r\n<p>缓存MySQL中的ResultSet,也就是一条SQL语句执行的结果集,所以仅仅只能针对select语句。当某个表的数据有任何任何变化,都会导致所有引用了该表的select语句在Query Cache中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用Query Cache可能会得不偿失。根据命中率(Qcachehits/(Qcachehits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB可能已经差不多了,大型的配置型静态数据可适当调大.</p>\r\n<p>可以通过命令show status like \'Qcache_%\'查看目前系统Query catch使用大小</p>\r\n<p>readbuffersize</p>\r\n<p>MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,可以通过增加该变量值以及内存缓冲区大小提高其性能</p>\r\n<p>sortbuffersize</p>\r\n<p>MySql执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能,可以尝试增加sortbuffersize变量的大小</p>\r\n<p>readrndbuffer_size</p>\r\n<p>MySql的随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。</p>\r\n<p>record_buffer</p>\r\n<p>每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,可能想要增加该值</p>\r\n<p>threadcachesize</p>\r\n<p>保存当前没有与连接关联但是准备为后面新的连接服务的线程,可以快速响应连接的线程请求而无需创建新的</p>\r\n<p>table_cache</p>\r\n<p>类似于threadcachesize,但用来缓存表文件,对InnoDB效果不大,主要用于MyISAM</p>\r\n</body>\r\n</html>','tinymce'),(4,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求。因此,Redis 基于内存存储数据,可以极大的提高查询性能,对产品在架构上很好的补充。在某些场景下,可以充分的利用 Redis 的特性,大大提高效率。</p>\r\n<p>缓存<br />对于热点数据,缓存以后可能读取数十万次,因此,对于热点数据,缓存的价值非常大。例如,分类栏目更新频率不高,但是绝大多数的页面都需要访问这个数据,因此读取频率相当高,可以考虑基于 Redis 实现缓存。</p>\r\n<p>会话缓存<br />此外,还可以考虑使用 Redis 进行会话缓存。例如,将 web session 存放在 Redis 中。</p>\r\n<p>时效性<br />例如验证码只有60秒有效期,超过时间无法使用,或者基于 Oauth2 的 Token 只能在 5 分钟内使用一次,超过时间也无法使用。</p>\r\n<p>访问频率<br />出于减轻服务器的压力或防止恶意的洪水攻击的考虑,需要控制访问频率,例如限制 IP 在一段时间的最大访问量。</p>\r\n<p>计数器<br />数据统计的需求非常普遍,通过原子递增保持计数。例如,应用数、资源数、点赞数、收藏数、分享数等。</p>\r\n<p>社交列表<br />社交属性相关的列表信息,例如,用户点赞列表、用户分享列表、用户收藏列表、用户关注列表、用户粉丝列表等,使用 Hash 类型数据结构是个不错的选择。</p>\r\n<p>记录用户判定信息<br />记录用户判定信息的需求也非常普遍,可以知道一个用户是否进行了某个操作。例如,用户是否点赞、用户是否收藏、用户是否分享等。</p>\r\n<p>交集、并集和差集<br />在某些场景中,例如社交场景,通过交集、并集和差集运算,可以非常方便地实现共同好友,共同关注,共同偏好等社交关系。</p>\r\n<p>热门列表与排行榜<br />按照得分进行排序,例如,展示最热、点击率最高、活跃度最高等条件的排名列表。</p>\r\n<p>最新动态<br />按照时间顺序排列的最新动态,也是一个很好的应用,可以使用 Sorted Set 类型的分数权重存储 Unix 时间戳进行排序。</p>\r\n<p>消息队列<br />Redis 能作为一个很好的消息队列来使用,依赖 List 类型利用 LPUSH 命令将数据添加到链表头部,通过 BRPOP 命令将元素从链表尾部取出。同时,市面上成熟的消息队列产品有很多,例如 RabbitMQ。因此,更加建议使用 RabbitMQ 作为消息中间件。</p>\r\n<p>Redis还有什么其它的使用场景么?欢迎下方留言探讨。<br /><br /></p>\r\n</body>\r\n</html>','tinymce'),(5,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190522085920857.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n<p>分布式锁一般有三种实现方式:1.&nbsp;数据库乐观锁;2.&nbsp;基于Redis的分布式锁;3.&nbsp;基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。&nbsp;</p>\r\n<h2><a name=\"t0\"></a>可靠性</h2>\r\n<p>首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:</p>\r\n<ul>\r\n<li>\r\n<p>互斥性。在任意时刻,只有一个客户端能持有锁。</p>\r\n</li>\r\n<li>\r\n<p>不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。</p>\r\n</li>\r\n<li>\r\n<p>具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。</p>\r\n</li>\r\n<li>\r\n<p>解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。</p>\r\n</li>\r\n</ul>\r\n<p>首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:</p>\r\n<pre class=\"language-java\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;redis.clients&lt;/groupId&gt;\r\n &lt;artifactId&gt;jedis&lt;/artifactId&gt;\r\n &lt;version&gt;2.9.0&lt;/version&gt;\r\n&lt;/dependency&gt;</code></pre>\r\n<p>Talk&nbsp;is&nbsp;cheap,&nbsp;show&nbsp;me&nbsp;the&nbsp;code。先展示代码,再带大家慢慢解释为什么这样实现:</p>\r\n<pre class=\"language-java\"><code>public class RedisTool {\r\n \r\n private static final String LOCK_SUCCESS = \"OK\";\r\n private static final String SET_IF_NOT_EXIST = \"NX\";\r\n private static final String SET_WITH_EXPIRE_TIME = \"PX\";\r\n \r\n /**\r\n * 尝试获取分布式锁\r\n * @param jedis Redis客户端\r\n * @param lockKey 锁\r\n * @param requestId 请求标识\r\n * @param expireTime 超期时间\r\n * @return 是否获取成功\r\n */\r\n public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {\r\n \r\n String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);\r\n \r\n if (LOCK_SUCCESS.equals(result)) {\r\n return true;\r\n }\r\n return false;\r\n \r\n }\r\n \r\n}</code></pre>\r\n<p>可以看到,我们加锁就一行代码:jedis.set(String&nbsp;key,&nbsp;String&nbsp;value,&nbsp;String&nbsp;nxxx,&nbsp;String&nbsp;expx,&nbsp;int&nbsp;time),这个set()方法一共有五个形参:</p>\r\n<p><strong>第一个为key</strong>,我们使用key来当锁,因为key是唯一的。</p>\r\n<p><strong>第二个为value</strong>,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。</p>\r\n<p><strong>第三个为nxxx</strong>,这个参数我们填的是NX,意思是SET&nbsp;IF&nbsp;NOT&nbsp;EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;</p>\r\n<p><strong>第四个为expx</strong>,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。</p>\r\n<p><strong>第五个为time</strong>,与第四个参数相呼应,代表key的过期时间。</p>\r\n<p>总的来说,执行上面的set()方法就只会导致两种结果:</p>\r\n<ol>\r\n<li>\r\n<p>当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。</p>\r\n</li>\r\n<li>\r\n<p>已有锁存在,不做任何操作。</p>\r\n</li>\r\n</ol>\r\n<p>心细的童鞋就会发现了,我们的加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。</p>\r\n<p>最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。</p>\r\n<p>错误示例1</p>\r\n<p>比较常见的错误示例就是使用jedis.setnx()和jedis.expire()组合实现加锁,代码如下:</p>\r\n<pre class=\"language-java\"><code>public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {\r\n \r\n Long result = jedis.setnx(lockKey, requestId);\r\n if (result == 1) {\r\n // 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁\r\n jedis.expire(lockKey, expireTime);\r\n }\r\n \r\n}</code></pre>\r\n<p>setnx()方法作用就是SET&nbsp;IF&nbsp;NOT&nbsp;EXIST,expire()方法就是给锁加一个过期时间。乍一看好像和前面的set()方法结果一样,然而由于这是两条Redis命令,不具有原子性,如果程序在执行完setnx()之后突然崩溃,导致锁没有设置过期时间。那么将会发生死锁。网上之所以有人这样实现,是因为低版本的jedis并不支持多参数的set()方法。</p>\r\n<p>错误示例2</p>\r\n<p>这一种错误示例就比较难以发现问题,而且实现也比较复杂。实现思路:使用jedis.setnx()命令实现加锁,其中key是锁,value是锁的过期时间。</p>\r\n<p>执行过程:</p>\r\n<ol>\r\n<li>\r\n<p>通过setnx()方法尝试加锁,如果当前锁不存在,返回加锁成功。</p>\r\n</li>\r\n<li>\r\n<p>如果锁已经存在则获取锁的过期时间,和当前时间比较,如果锁已经过期,则设置新的过期时间,返回加锁成功。</p>\r\n</li>\r\n</ol>\r\n<p>代码如下:</p>\r\n<pre class=\"language-java\"><code>public static boolean wrongGetLock2(Jedis jedis, String lockKey, int expireTime) {\r\n \r\n long expires = System.currentTimeMillis() + expireTime;\r\n String expiresStr = String.valueOf(expires);\r\n \r\n // 如果当前锁不存在,返回加锁成功\r\n if (jedis.setnx(lockKey, expiresStr) == 1) {\r\n return true;\r\n }\r\n \r\n // 如果锁存在,获取锁的过期时间\r\n String currentValueStr = jedis.get(lockKey);\r\n if (currentValueStr != null &amp;&amp; Long.parseLong(currentValueStr) &lt; System.currentTimeMillis()) {\r\n // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间\r\n String oldValueStr = jedis.getSet(lockKey, expiresStr);\r\n if (oldValueStr != null &amp;&amp; oldValueStr.equals(currentValueStr)) {\r\n // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才有权利加锁\r\n return true;\r\n }\r\n }\r\n \r\n // 其他情况,一律返回加锁失败\r\n return false;\r\n \r\n}</code></pre>\r\n<p>那么这段代码问题在哪里?1.&nbsp;由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步。&nbsp;2.&nbsp;当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖。3.&nbsp;锁不具备拥有者标识,即任何客户端都可以解锁。</p>\r\n<h3><a name=\"t1\"></a>解锁代码</h3>\r\n<p>正确姿势</p>\r\n<p>还是先展示代码,再带大家慢慢解释为什么这样实现:</p>\r\n<pre class=\"language-java\"><code>public class RedisTool {\r\n \r\n private static final Long RELEASE_SUCCESS = 1L;\r\n \r\n /**\r\n * 释放分布式锁\r\n * @param jedis Redis客户端\r\n * @param lockKey 锁\r\n * @param requestId 请求标识\r\n * @return 是否释放成功\r\n */\r\n public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {\r\n \r\n String script = \"if redis.call(\'get\', KEYS[1]) == ARGV[1] then return redis.call(\'del\', KEYS[1]) else return 0 end\";\r\n Object result = jedis., Collections.singletonList(requestId));\r\n \r\n if (RELEASE_SUCCESS.equals(result)) {\r\n return true;\r\n }\r\n return false;\r\n \r\n }\r\n \r\n}</code></pre>\r\n<p>可以看到,我们解锁只需要两行代码就搞定了!第一行代码,我们写了一个简单的Lua脚本代码,上一次见到这个编程语言还是在《黑客与画家》里,没想到这次居然用上了。第二行代码,我们将Lua代码传到jedis.方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。方法是将Lua代码交给Redis服务端执行。</p>\r\n<p>那么这段Lua代码的功能是什么呢?其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述操作是原子性的。关于非原子性会带来什么问题,可以阅读【解锁代码-错误示例2】&nbsp;。那么为什么执行方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:</p>\r\n<p>简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。</p>\r\n<p>错误示例1</p>\r\n<p>最常见的解锁代码就是直接使用jedis.del()方法删除锁,这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,即使这把锁不是它的。</p>\r\n<pre class=\"language-java\"><code>public static void wrongReleaseLock1(Jedis jedis, String lockKey) {\r\n jedis.del(lockKey);\r\n}</code></pre>\r\n<p>错误示例2</p>\r\n<p>这种解锁代码乍一看也是没问题,甚至我之前也差点这样实现,与正确姿势差不多,唯一区别的是分成两条命令去执行,代码如下:</p>\r\n<pre class=\"language-java\"><code>public static void wrongReleaseLock2(Jedis jedis, String lockKey, String requestId) {\r\n \r\n // 判断加锁与解锁是不是同一个客户端\r\n if (requestId.equals(jedis.get(lockKey))) {\r\n // 若在此时,这把锁突然不是这个客户端的,则会误解锁\r\n jedis.del(lockKey);\r\n }\r\n \r\n}</code></pre>\r\n<p>如代码注释,问题在于如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁。那么是否真的有这种场景?答案是肯定的,比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。</p>\r\n<h2><a name=\"t2\"></a>总结</h2>\r\n<p>本文主要介绍了如何使用Java代码正确实现Redis分布式锁,对于加锁和解锁也分别给出了两个比较经典的错误示例。其实想要通过Redis实现分布式锁并不难,只要保证能满足可靠性里的四个条件。互联网虽然给我们带来了方便,只要有问题就可以google,然而网上的答案一定是对的吗?其实不然,所以我们更应该时刻保持着质疑精神,多想多验证。</p>\r\n<p>如果你的项目中Redis是多机部署的,那么可以尝试使用Redisson实现分布式锁,这是Redis官方提供的Java组件。</p>\r\n</body>\r\n</html>','tinymce'),(6,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<pre class=\"language-java\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;javax&lt;/groupId&gt;\r\n &lt;artifactId&gt;javaee-api&lt;/artifactId&gt;\r\n &lt;version&gt;7.0&lt;/version&gt;\r\n &lt;scope&gt;provided&lt;/scope&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;\r\n &lt;artifactId&gt;gson&lt;/artifactId&gt;\r\n &lt;version&gt;2.8.2&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.quartz-scheduler&lt;/groupId&gt;\r\n &lt;artifactId&gt;quartz&lt;/artifactId&gt;\r\n &lt;version&gt;2.2.1&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;\r\n &lt;artifactId&gt;lombok&lt;/artifactId&gt;\r\n &lt;optional&gt;true&lt;/optional&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.baomidou&lt;/groupId&gt;\r\n &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;\r\n &lt;version&gt;3.1.1&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;!-- mybatis-plus begin --&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;mysql&lt;/groupId&gt;\r\n &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;\r\n &lt;scope&gt;runtime&lt;/scope&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;\r\n &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;\r\n &lt;version&gt;3.8.1&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba&lt;/groupId&gt;\r\n &lt;artifactId&gt;fastjson&lt;/artifactId&gt;\r\n &lt;version&gt;1.2.54&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba&lt;/groupId&gt;\r\n &lt;artifactId&gt;druid&lt;/artifactId&gt;\r\n &lt;version&gt;1.1.9&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt;\r\n &lt;artifactId&gt;httpclient&lt;/artifactId&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-context-support&lt;/artifactId&gt;\r\n &lt;version&gt;5.1.5.RELEASE&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;io.springfox&lt;/groupId&gt;\r\n &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;\r\n &lt;version&gt;2.2.2&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;io.springfox&lt;/groupId&gt;\r\n &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;\r\n &lt;version&gt;2.2.2&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;io.springfox&lt;/groupId&gt;\r\n &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;\r\n &lt;version&gt;2.7.0&lt;/version&gt;\r\n &lt;/dependency&gt;</code></pre>\r\n<pre class=\"language-java\"><code>package com.example.socket.controller;\r\n \r\nimport com.example.socket.common.ResultMap;\r\nimport com.example.socket.common.ValidatorUtils;\r\nimport com.example.socket.model.PageUtils;\r\nimport com.example.socket.model.ScheduleJobEntity;\r\nimport com.example.socket.service.IScheduleJobService;\r\nimport io.swagger.annotations.Api;\r\nimport io.swagger.annotations.ApiOperation;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.bind.annotation.*;\r\n \r\nimport java.util.Map;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/5/20\r\n * Time:14:37\r\n */\r\n@Api(value = \"定时任务\", tags = {\" 定时任务\"})\r\n@RestController\r\n@RequestMapping(\"/sys/schedule\")\r\npublic class QuartzApiController {\r\n \r\n \r\n @Autowired\r\n private IScheduleJobService scheduleJobService;\r\n \r\n /**\r\n * 定时任务列表\r\n */\r\n @ApiOperation(value = \"定时任务列表\", notes = \"定时任务列表\")\r\n @GetMapping(\"/list\")\r\n public ResultMap list(@RequestParam Map&lt;String, Object&gt; params) {\r\n PageUtils page = scheduleJobService.queryPage(params);\r\n \r\n return ResultMap.ok().put(\"page\", page);\r\n }\r\n \r\n /**\r\n * 定时任务信息\r\n */\r\n @ApiOperation(value = \"定时任务信息\", notes = \"定时任务信息\")\r\n @GetMapping(\"/info/{jobId}\")\r\n public ResultMap info(@PathVariable(\"jobId\") Long jobId) {\r\n ScheduleJobEntity schedule = scheduleJobService.getById(jobId);\r\n \r\n return ResultMap.ok().put(\"schedule\", schedule);\r\n }\r\n \r\n /**\r\n * 保存定时任务\r\n */\r\n @ApiOperation(value = \"保存定时任务\", notes = \"保存定时任务\")\r\n @PostMapping(\"/save\")\r\n public ResultMap save(@RequestBody ScheduleJobEntity scheduleJob) {\r\n ValidatorUtils.validateEntity(scheduleJob);\r\n \r\n scheduleJobService.insert(scheduleJob);\r\n \r\n return ResultMap.ok();\r\n }\r\n \r\n /**\r\n * 修改定时任务\r\n */\r\n @ApiOperation(value = \"修改定时任务\", notes = \"修改定时任务\")\r\n @PostMapping(\"/update\")\r\n public ResultMap update(@RequestBody ScheduleJobEntity scheduleJob) {\r\n //ValidatorUtils.validateEntity(scheduleJob);\r\n \r\n scheduleJobService.update(scheduleJob);\r\n \r\n return ResultMap.ok();\r\n }\r\n \r\n /**\r\n * 删除定时任务\r\n */\r\n @ApiOperation(value = \"删除定时任务\", notes = \"删除定时任务\")\r\n @PostMapping(\"/delete\")\r\n public ResultMap delete(@RequestBody Long[] jobIds) {\r\n scheduleJobService.deleteBatch(jobIds);\r\n \r\n return ResultMap.ok();\r\n }\r\n \r\n /**\r\n * 立即执行任务\r\n */\r\n @ApiOperation(value = \"立即执行任务\", notes = \"立即执行任务\")\r\n @PostMapping(\"/run\")\r\n public ResultMap run(@RequestBody Long[] jobIds) {\r\n scheduleJobService.run(jobIds);\r\n \r\n return ResultMap.ok();\r\n }\r\n \r\n /**\r\n * 暂停定时任务\r\n */\r\n @ApiOperation(value = \"暂停定时任务\", notes = \"暂停定时任务\")\r\n @PostMapping(\"/pause\")\r\n public ResultMap pause(@RequestBody Long[] jobIds) {\r\n scheduleJobService.pause(jobIds);\r\n \r\n return ResultMap.ok();\r\n }\r\n \r\n /**\r\n * 恢复定时任务\r\n */\r\n @ApiOperation(value = \"恢复定时任务\", notes = \"恢复定时任务\")\r\n @PostMapping(\"/resume\")\r\n public ResultMap resume(@RequestBody Long[] jobIds) {\r\n scheduleJobService.resume(jobIds);\r\n \r\n return ResultMap.ok();\r\n }\r\n}</code></pre>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190520165758737.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" />.</p>\r\n<p>&nbsp;</p>\r\n<p>代码就不一一贴了代价可以去下载源码:<a href=\"https://gitee.com/qwerdfs/springboot_common\" target=\"_blank\" rel=\"nofollow noopener\">https://gitee.com/qwerdfs/springboot_common</a></p>\r\n</body>\r\n</html>','tinymce'),(7,'<p>实参(argument):</p>\r\n<p>&nbsp;&nbsp;全称为\"实际参数\"是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。&nbsp;&nbsp; &nbsp; &nbsp;</p>\r\n<p>&nbsp;</p>\r\n<p>形参(parameter):</p>\r\n<p>全称为\"形式参数\" 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值。</p>\r\n<p>&nbsp;</p>\r\n<p>形式参数:形参是函数被调用时用于接收实参值的变量。</p>\r\n<p>根据实际需要可有可无。没有形参时,圆括号也不可省;多个参数之间应用逗号分隔。参数包括参数名和参数类型。</p>\r\n<p>形参的类型说明可有如下两种格式:</p>\r\n<p>&nbsp; int&nbsp;max(int&nbsp; a,int&nbsp;b)/*形参的类型在形参表中直接说明*/</p>\r\n<p>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;return (a&gt;b?a:b);}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>\r\n<p>&nbsp;或</p>\r\n<p>&nbsp;&nbsp;&nbsp;int max(a,b)</p>\r\n<p>&nbsp;&nbsp;&nbsp;inta,b;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*形参的类型在函数体前、函数名后说明*/</p>\r\n<p>&nbsp;&nbsp; { return(a&gt;b?a:b); }</p>\r\n<p>前者为标准格式,后者为传统格式,通常用前者。</p>\r\n<p>&nbsp;</p>\r\n<p><strong>形参和实参的区别</strong></p>\r\n<p>形参出现在<strong>函数定义</strong>中,在整个函数体内都可以使用,&nbsp;离开该函数则不能使用。</p>\r\n<p>实参出现在<strong>主调函数中,进入被调函数后,实参变量也不能使用</strong>。&nbsp;</p>\r\n<p>形参和实参的功能是作数据传送。发生函数调用时,&nbsp;<strong>主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送</strong>。</p>\r\n<p>1.形参变量只有在被调用时才分配内存单元,<strong>在调用结束时,</strong><strong>&nbsp;</strong><strong>即刻释放所分配的内存单元</strong>。因此,形参只有在函数内部有效。&nbsp;函数调用结束返回主调函数后则不能再使用该形参变量。&nbsp;</p>\r\n<p>2.实参可以是常量、变量、表达式、函数等,&nbsp;无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,&nbsp;以便把这些值传送给形参。&nbsp;因此应预先用赋值,输入等办法使实参获得确定值。&nbsp;</p>\r\n<p>3.实参和形参在数量上,类型上,顺序上应严格一致,&nbsp;否则会发生&ldquo;类型不匹配&rdquo;的错误。&nbsp;</p>\r\n<p>4.<strong>函数调用中发生的数据传送是单向的</strong>。&nbsp;即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。&nbsp;因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。</p>\r\n<p>5.当形参和实参不是指针类型时,在该函数运行时,<strong>形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变</strong>。</p>\r\n<p>而<strong>如果函数的参数是指针类型变量</strong><strong>,</strong><strong>在调用该函数的过程中,传给函数的是实参的地址,在函数体内部使用的也是实参的地址,即使用的就是实参本身</strong>。所以在函数体内部可以改变实参的值。</p>\r\n<p>&nbsp;</p>','tinymce'),(8,'<pre class=\"language-python\"><code># 该程序实现刷CSDN网页访问量,当访问被拒绝或者遇到其他异常时会自动重启,无限刷\r\n# 经过测试发现大概间隔70秒访问一下,访问量才会增加1\r\n# 只需要修改或者添加url的链接就可以了\r\n \r\nimport urllib.request\r\nimport time\r\n \r\nurl = [\'https://blog.csdn.net/qq_37996327/article/details/89371749\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/89466220\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/89177643\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/89176697\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/88840667\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/88822720\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/88822629\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/86624654\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/86239546\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/86000743\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/85274935\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/85274010\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/84991769\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/84816175\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/84753623\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80856419\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80856227\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80810282\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80809937\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80809878\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80809856\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80809834\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/80851557\',\r\n \'https://blog.csdn.net/qq_37996327/article/details/84964639\']\r\n \r\ncountUrl = len(url)\r\ncount = 0\r\ncount1 = 0\r\n \r\n# 加入请求头\r\nheaders = {\r\n \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\'}\r\n \r\n# 让程序一直执行\r\nwhile True:\r\n if count1 &lt; 1000:\r\n try: # 正常运行\r\n count = count + 1\r\n print(count, \'times\') # 监视程序是否在正常运行,输出运行了多少次\r\n for i in range(countUrl): # 遍历所有url\r\n req = urllib.request.Request(url[i], headers=headers)\r\n urllib.request.urlopen(req) # 访问网页\r\n time.sleep(70) # 间隔执行\r\n \r\n except urllib.error.HTTPError: # 服务器异常\r\n print(\'HTTPError\')\r\n count1 = count1 + 1\r\n time.sleep(10)\r\n \r\n except urllib.error.URLError: # 链接异常\r\n print(\'URLError\')\r\n count1 = count1 + 1\r\n time.sleep(10)\r\n \r\n except Exception: # 其他异常\r\n print(\'Retry\')\r\n count1 = count1 + 1\r\n time.sleep(10)</code></pre>\r\n<p>&nbsp;</p>','tinymce'),(9,'<pre class=\"language-javascript\"><code>var xhr = new XMLHttpRequest();\r\n xhr.open(\'POST\', url, true); //第三个参数决定是否采用异步的方式\r\n xhr.send(data);\r\n xhr.onreadystatechange = function(){\r\n if(xhr.readystate === 4 &amp;&amp; xhr.status === 200){\r\n ///xxxx\r\n }\r\n }</code></pre>\r\n<pre class=\"language-javascript\"><code>//最原始的写法-同步写法\r\n \r\nf1(); //耗时很长,严重堵塞\r\nf2(); \r\nf3(); //导致f3执行受到影响\r\n \r\n \r\n//改进版-异步写法\r\nfunction f1(callback){\r\n  setTimeout(function () {\r\n    // f1的任务代码\r\n    callback();\r\n  }, 1000);\r\n}\r\n \r\nf1(f2); //\r\n \r\nf3();</code></pre>','tinymce'),(10,'<p>前言:最近小王同学又遇到了一个需求:线上的业务运行了一段时间,后来随着使用人数增多,出现了一个问题是这样的,一个订单会重复创建几次,导致数据库里出现了很多垃圾数据。在测试同学的不断测试下,发现问题出在了前端的一个提交按钮上,有的用户比较&ldquo;着急&rdquo;,提交订单时候,一下子快速的点了5、6次(这手速没有几年是练不出来的,咳咳),导致请求一下子都涌进来,后端进行了重复处理。项目经理让小王优化这个点,绝对不能创建多次。还有一个需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。</p>\r\n<p>&nbsp;</p>\r\n<p><strong>一:时间控制的几种方案</strong></p>\r\n<p>1.1: 从线程方面解决</p>\r\n<p>&nbsp;最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。</p>\r\n<p>1.2:使用Timer</p>\r\n<p>查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:</p>\r\n<pre class=\"has\"><code class=\"language-java hljs\"></code></pre>\r\n<pre class=\"language-java\"><code>public class TimmerTest {\r\n /**\r\n * 测试方法\r\n */\r\n public void test() {\r\n Timer timer = new Timer();\r\n timer.schedule(new MyTask(), 800);\r\n }\r\n \r\n public class MyTask extends TimerTask {\r\n \r\n /**\r\n * 运行方法\r\n */\r\n @Override\r\n public void run() {\r\n System.out.println(\"输出\");\r\n }\r\n }\r\n}</code></pre>\r\n<p>这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。</p>\r\n<p>1.3:redis延时</p>\r\n<p>在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:</p>\r\n<p><img src=\"https://img2018.cnblogs.com/blog/1066538/201901/1066538-20190106230842838-990730237.png\" /></p>\r\n<p>通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:</p>\r\n<p>1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制</p>\r\n<p>2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现</p>\r\n<p>3:简单,真正的代码实现起来只有很少,下面会给出代码示范。</p>\r\n<p><strong>二:redis</strong></p>\r\n<p><strong>2.1:maven中引入redis</strong></p>\r\n<p><strong>引入</strong>spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:</p>\r\n<pre class=\"language-java\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;\r\n &lt;exclusions&gt;\r\n &lt;exclusion&gt;\r\n &lt;groupId&gt;io.lettuce&lt;/groupId&gt;\r\n &lt;artifactId&gt;lettuce-core&lt;/artifactId&gt;\r\n &lt;/exclusion&gt;\r\n &lt;/exclusions&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;redis.clients&lt;/groupId&gt;\r\n &lt;artifactId&gt;jedis&lt;/artifactId&gt;\r\n &lt;/dependency&gt;</code></pre>\r\n<p><strong>2.2: 在springboot中配置redis</strong></p>\r\n<pre class=\"language-java\"><code>import org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.data.redis.core.RedisTemplate;\r\nimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;\r\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\r\n@Configuration\r\npublic class RedisConfig {\r\n \r\n @Autowired\r\n private RedisTemplate redisTemplate;\r\n \r\n /**\r\n * redisTemplate实例化\r\n *\r\n * @return\r\n */\r\n @Bean\r\n public RedisTemplate redisTemplateInit() {\r\n //设置序列化Key的实例化对象\r\n redisTemplate.setKeySerializer(new StringRedisSerializer());\r\n //设置序列化Value的实例化对象\r\n redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());\r\n return redisTemplate;\r\n }\r\n \r\n}</code></pre>\r\n<p><strong>2.2:redisTemplate模板工具类</strong></p>\r\n<pre class=\"language-javascript\"><code>@Component\r\npublic class RedisManager {\r\n \r\n private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);\r\n \r\n @Autowired\r\n private RedisTemplate redisTemplate;\r\n \r\n /**\r\n * 设置对象\r\n *\r\n * @param key key\r\n * @param value value值\r\n * @param &lt;T&gt; 返回值泛型\r\n * @return 正确的值:&lt;T&gt; 错误的值:null\r\n */\r\n @SuppressWarnings(\"unchecked\")\r\n public &lt;T&gt; ValueOperations&lt;String, T&gt; setObject(final String key, final T value) {\r\n final ValueOperations&lt;String, T&gt; operation = redisTemplate.opsForValue();\r\n operation.set(key, value);\r\n return operation;\r\n }\r\n \r\n /**\r\n * 设置对象及失效时间 (单位:秒)\r\n *\r\n * @param key key\r\n * @param value value值\r\n * @param &lt;T&gt; 返回值泛型\r\n * @param time 秒值\r\n * @return 正确的值:&lt;T&gt; 错误的值:null\r\n */\r\n @SuppressWarnings(\"unchecked\")\r\n public &lt;T&gt; ValueOperations&lt;String, T&gt; setObject(final String key, final T value, final long time) {\r\n final ValueOperations&lt;String, T&gt; operation = redisTemplate.opsForValue();\r\n operation.set(key, value, time, TimeUnit.SECONDS);\r\n return operation;\r\n }\r\n \r\n \r\n /**\r\n * 设置对象及失效时间(单位:毫秒)\r\n *\r\n * @param key key\r\n * @param value value值\r\n * @param &lt;T&gt; 返回值泛型\r\n * @param time 秒值\r\n * @return 正确的值:&lt;T&gt; 错误的值:null\r\n */\r\n @SuppressWarnings(\"unchecked\")\r\n public &lt;T&gt; ValueOperations&lt;String, T&gt; setObjectForMillSeconds(final String key, final T value, final long time) {\r\n final ValueOperations&lt;String, T&gt; operation = redisTemplate.opsForValue();\r\n operation.set(key, value, time, TimeUnit.MILLISECONDS);\r\n return operation;\r\n }\r\n \r\n /**\r\n * 获取对象\r\n *\r\n * @param key 键\r\n * @return 正确的值:Object值对象&lt;br&gt;\r\n * 错误的值:null\r\n */\r\n @SuppressWarnings(\"unchecked\")\r\n public Object getObject(final String key) {\r\n final ValueOperations&lt;String, Object&gt; valueOperations = redisTemplate.opsForValue();\r\n if (valueOperations == null || !redisTemplate.hasKey(key)) {\r\n return null;\r\n }\r\n final Object object = valueOperations.get(key);\r\n return object;\r\n }\r\n \r\n /**\r\n * 从缓存中获取string值\r\n *\r\n * @param key\r\n * @return*/\r\n @SuppressWarnings(\"unchecked\")\r\n public String getString(final String key) {\r\n String value = \"\";\r\n final ValueOperations&lt;String, Object&gt; valueOperations = redisTemplate.opsForValue();\r\n if (valueOperations != null &amp;&amp; redisTemplate.hasKey(key)) {\r\n final Object object = valueOperations.get(key);\r\n if (null != object) {\r\n LOGGER.info(\"--getString--object not empty\");\r\n value = object.toString();\r\n } else {\r\n LOGGER.info(\"--getString--object empty\");\r\n }\r\n }\r\n return value;\r\n }</code></pre>\r\n<p><strong>2.2:在redis中实现时间控制</strong></p>\r\n<p><strong>2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值</strong></p>\r\n<pre class=\"language-html\"><code>import com.youjia.orders.redis.RedisManager;\r\nimport org.junit.Test;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\n \r\nimport java.util.Objects;\r\n \r\n/**\r\n * @Auther: Yrion\r\n * @Date: 2019-01-11 23:36\r\n */\r\n \r\npublic class RedisTest extends OrderProviderApplicationTests {\r\n \r\n @Autowired\r\n private RedisManager redisManager;\r\n \r\n @Test\r\n public void test() {\r\n controlTime(\"10000001\", 10L);\r\n }\r\n \r\n public void controlTime(String requestId, Long timeOut) {\r\n \r\n if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {\r\n return;\r\n }\r\n //something code\r\n final String value = \"value\";\r\n redisManager.setObject(requestId, value, timeOut);\r\n final long startTime = System.currentTimeMillis();\r\n System.out.println(\"开始控制时间\");\r\n //start\r\n for (; ; ) {\r\n if (Objects.isNull(redisManager.getObject(requestId))) {\r\n break;\r\n }\r\n }\r\n final long endTime = System.currentTimeMillis();\r\n \r\n final long useTime = endTime - startTime;\r\n \r\n System.out.println(\"一共耗费时间:\" + useTime);\r\n }\r\n}</code></pre>\r\n<p>outPut:</p>\r\n<pre>开始控制时间\r\n一共耗费时间:10042</pre>\r\n<pre class=\"language-html\"><code>/**\r\n * 防止提交过于频繁\r\n */\r\n public void preventFrequentSubmit(Long orderId) throws Exception{\r\n \r\n //some code\r\n final String key = String.valueOf(orderId);\r\n \r\n final String value = \"orderValue\";\r\n \r\n if (redisManager.getObject(String.valueOf(orderId))!=null){\r\n throw new Exception(\"提交订单太过频繁,请10秒后重试!\");\r\n }else {\r\n redisManager.setObject(orderId,value,10);\r\n }\r\n \r\n //continue\r\n \r\n }</code></pre>\r\n<p><strong>三:总结</strong></p>\r\n<p><strong>&nbsp;本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!</strong></p>\r\n<pre class=\"has\"><code class=\"language-java hljs\"></code></pre>','tinymce'),(11,'<h3><strong>需要在get方法上面加入&nbsp;</strong></h3>\r\n<h3><strong>@JsonFormat(locale=\"zh\", timezone=\"GMT+8\", pattern=\"yyyy-MM-dd HH:mm:ss\")</strong></h3>\r\n<h3><a name=\"t2\"></a><strong>&nbsp;这个注解就可以了</strong></h3>\r\n<p><strong><img src=\"https://img-blog.csdnimg.cn/20190423093914717.png\" /></strong></p>\r\n<p><strong><img src=\"https://img-blog.csdnimg.cn/20190423094058795.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></strong></p>','tinymce'),(12,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>一、Maven依赖,我把所有的依赖贴出来了,大家可以自行删减</p>\r\n<pre class=\"language-html\"><code> &lt;dependencies&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;\r\n &lt;scope&gt;test&lt;/scope&gt;\r\n &lt;/dependency&gt;\r\n &lt;!--前端--&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;net.sourceforge.nekohtml&lt;/groupId&gt;\r\n &lt;artifactId&gt;nekohtml&lt;/artifactId&gt;\r\n &lt;version&gt;1.9.22&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.javassist&lt;/groupId&gt;\r\n &lt;artifactId&gt;javassist&lt;/artifactId&gt;\r\n &lt;version&gt;3.22.0-GA&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;\r\n &lt;version&gt;5.1.2.RELEASE&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;mysql&lt;/groupId&gt;\r\n &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;\r\n &lt;version&gt;5.1.43&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba&lt;/groupId&gt;\r\n &lt;artifactId&gt;fastjson&lt;/artifactId&gt;\r\n &lt;version&gt;1.2.51&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n \r\n &lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba&lt;/groupId&gt;\r\n &lt;artifactId&gt;druid&lt;/artifactId&gt;\r\n &lt;version&gt;1.1.9&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;/dependencies&gt;</code></pre>\r\n<p>二、拦截器</p>\r\n<pre class=\"language-html\"><code>package com.ctg.test.cros.springbootcros.filter;\r\n \r\nimport com.alibaba.fastjson.JSONObject;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n \r\nimport javax.servlet.*;\r\nimport javax.servlet.annotation.WebFilter;\r\nimport javax.servlet.http.Cookie;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.io.IOException;\r\nimport java.util.Arrays;\r\n \r\n/**\r\n * 跨域:由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的 数据接口;\r\n * 前后端都需要设置允许跨域\r\n */\r\n@WebFilter\r\npublic class CrosXssFilter implements Filter {\r\n // 多个跨域域名设置\r\n public static final String[] ALLOW_DOMAIN = {\"http://localhost:8080\",\r\n \"http://localhost:8090\", \"http://localhost:8081\",\"http://127.0.0.1:8020\"};\r\n private static final Logger logger = LoggerFactory.getLogger(CrosXssFilter.class);\r\n \r\n @Override\r\n public void init(FilterConfig filterConfig) throws ServletException {\r\n }\r\n \r\n @Override\r\n public void doFilter(ServletRequest request, ServletResponse response,\r\n FilterChain chain) throws IOException, ServletException {\r\n request.setCharacterEncoding(\"utf-8\");\r\n response.setContentType(\"text/html;charset=utf-8\");\r\n //跨域设置\r\n HttpServletRequest req = (HttpServletRequest) request;\r\n HttpServletResponse res = (HttpServletResponse) response;\r\n String originHeader = req.getHeader(\"Origin\");\r\n if (Arrays.asList(ALLOW_DOMAIN).contains(originHeader)) {\r\n //通过在响应 header 中设置 &lsquo;*&rsquo; 来允许来自所有域的跨域请求访问。\r\n res.setHeader(\"Access-Control-Allow-Origin\", originHeader);\r\n //通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie\r\n //设置了Allow-Credentials,Allow-Origin就不能为*,需要指明具体的url域\r\n res.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\r\n //请求方式\r\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\r\n //(预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久\r\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\r\n //首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段\r\n //res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\r\n res.setHeader(\"Access-Control-Allow-Headers\", \"Timestamp,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Access-Control-Allow-Headers\");\r\n }\r\n //sql,xss过滤\r\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\r\n Cookie[] cookies = httpServletRequest.getCookies();\r\n logger.info(\".......sessionid:{},session:{}\",httpServletRequest.getSession().getId(), JSONObject.toJSONString(httpServletRequest.getSession().getAttributeNames()));\r\n logger.info(\".......,cookie:{}\",JSONObject.toJSONString(httpServletRequest.getCookies()));\r\n \r\n logger.info(\"CrosXssFilter.......orignal url:{},ParameterMap:{}\", httpServletRequest.getRequestURI(), JSONObject.toJSONString(httpServletRequest.getParameterMap()));\r\n XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(\r\n httpServletRequest);\r\n chain.doFilter(xssHttpServletRequestWrapper, response);\r\n logger.info(\"CrosXssFilter..........doFilter url:{},ParameterMap:{}\", xssHttpServletRequestWrapper.getRequestURI(), JSONObject.toJSONString(xssHttpServletRequestWrapper.getParameterMap()));\r\n }\r\n \r\n @Override\r\n public void destroy() {\r\n \r\n }\r\n \r\n}</code></pre>\r\n<p>3.防止sql注入</p>\r\n<pre class=\"language-java\"><code>package com.ctg.test.cros.springbootcros.filter;\r\n \r\n \r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.util.StringUtils;\r\n \r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletRequestWrapper;\r\nimport java.util.HashMap;\r\nimport java.util.HashSet;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\n \r\n/**\r\n * 防止sql注入,xss攻击\r\n * 前端可以对输入信息做预处理,后端也可以做处理。\r\n */\r\npublic class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {\r\n private final Logger log = LoggerFactory.getLogger(getClass());\r\n private static String key = \"and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+\";\r\n private static Set&lt;String&gt; notAllowedKeyWords = new HashSet&lt;String&gt;(0);\r\n private static String replacedString=\"INVALID\";\r\n static {\r\n String keyStr[] = key.split(\"\\\\|\");\r\n for (String str : keyStr) {\r\n notAllowedKeyWords.add(str);\r\n }\r\n }\r\n \r\n private String currentUrl;\r\n \r\n public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {\r\n super(servletRequest);\r\n currentUrl = servletRequest.getRequestURI();\r\n }\r\n \r\n \r\n /**覆盖getParameter方法,将参数名和参数值都做xss过滤。\r\n * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取\r\n * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖\r\n */\r\n @Override\r\n public String getParameter(String parameter) {\r\n String value = super.getParameter(parameter);\r\n if (value == null) {\r\n return null;\r\n }\r\n return cleanXSS(value);\r\n }\r\n @Override\r\n public String[] getParameterValues(String parameter) {\r\n String[] values = super.getParameterValues(parameter);\r\n if (values == null) {\r\n return null;\r\n }\r\n int count = values.length;\r\n String[] encodedValues = new String[count];\r\n for (int i = 0; i &lt; count; i++) {\r\n encodedValues[i] = cleanXSS(values[i]);\r\n }\r\n return encodedValues;\r\n }\r\n @Override\r\n public Map&lt;String, String[]&gt; getParameterMap(){\r\n Map&lt;String, String[]&gt; values=super.getParameterMap();\r\n if (values == null) {\r\n return null;\r\n }\r\n Map&lt;String, String[]&gt; result=new HashMap&lt;&gt;();\r\n for(String key:values.keySet()){\r\n String encodedKey=cleanXSS(key);\r\n int count=values.get(key).length;\r\n String[] encodedValues = new String[count];\r\n for (int i = 0; i &lt; count; i++){\r\n encodedValues[i]=cleanXSS(values.get(key)[i]);\r\n }\r\n result.put(encodedKey,encodedValues);\r\n }\r\n return result;\r\n }\r\n /**\r\n * 覆盖getHeader方法,将参数名和参数值都做xss过滤。\r\n * 如果需要获得原始的值,则通过super.getHeaders(name)来获取\r\n * getHeaderNames 也可能需要覆盖\r\n */\r\n @Override\r\n public String getHeader(String name) {\r\n String value = super.getHeader(name);\r\n if (value == null) {\r\n return null;\r\n }\r\n return cleanXSS(value);\r\n }\r\n \r\n private String cleanXSS(String valueP) {\r\n // You\'ll need to remove the spaces from the html entities below\r\n String value = valueP.replaceAll(\"&lt;\", \"&amp;lt;\").replaceAll(\"&gt;\", \"&amp;gt;\");\r\n value = value.replaceAll(\"&lt;\", \"&amp; lt;\").replaceAll(\"&gt;\", \"&amp; gt;\");\r\n value = value.replaceAll(\"\\\\(\", \"&amp; #40;\").replaceAll(\"\\\\)\", \"&amp; #41;\");\r\n value = value.replaceAll(\"\'\", \"&amp; #39;\");\r\n value = value.replaceAll(\"eval\\\\((.*)\\\\)\", \"\");\r\n value = value.replaceAll(\"[\\\\\\\"\\\\\\\'][\\\\s]*(.*)[\\\\\\\"\\\\\\\']\", \"\\\"\\\"\");\r\n value = value.replaceAll(\"script\", \"\");\r\n value = cleanSqlKeyWords(value);\r\n return value;\r\n }\r\n \r\n private String cleanSqlKeyWords(String value) {\r\n String paramValue = value;\r\n for (String keyword : notAllowedKeyWords) {\r\n if (paramValue.length() &gt; keyword.length() + 4\r\n &amp;&amp; (paramValue.contains(\" \"+keyword)||paramValue.contains(keyword+\" \")||paramValue.contains(\" \"+keyword+\" \"))) {\r\n paramValue = StringUtils.replace(paramValue, keyword, replacedString);\r\n log.error(this.currentUrl + \"已被过滤,因为参数中包含不允许sql的关键词(\" + keyword\r\n + \")\"+\";参数:\"+value+\";过滤后的参数:\"+paramValue);\r\n }\r\n }\r\n return paramValue;\r\n }\r\n \r\n}</code></pre>\r\n<pre class=\"language-html\"><code>package com.ctg.test.cros.springbootcros.util;\r\n \r\nimport javax.servlet.http.Cookie;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n/**\r\n * @Description: TODO\r\n * @Author: qw\r\n * @Date: 2018/11/30 11:20\r\n */\r\npublic class CookieUtils {\r\n \r\n public static String getCookie(HttpServletRequest request,String cookieName){\r\n \r\n Cookie[] cookies = request.getCookies();\r\n if(cookies != null){\r\n for(Cookie cookie : cookies){\r\n if(cookie.getName().equals(cookieName)){\r\n return cookie.getValue();\r\n }\r\n }\r\n }\r\n \r\n return null;\r\n }\r\n //设置cookie域\r\n public static void writeCookie(HttpServletResponse response, String cookieName, String value){\r\n Cookie cookie = new Cookie(cookieName,value);\r\n cookie.setPath(\"/\");\r\n cookie.setMaxAge(3600);\r\n response.addCookie(cookie);\r\n }\r\n \r\n}</code></pre>\r\n<pre class=\"language-html\"><code>package com.ctg.test.cros.springbootcros;\r\n \r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\nimport org.springframework.boot.web.servlet.ServletComponentScan;\r\n \r\n@SpringBootApplication\r\n@ServletComponentScan //触发过滤器\r\npublic class SpringbootCrosApplication {\r\n \r\n public static void main(String[] args) {\r\n SpringApplication.run(SpringbootCrosApplication.class, args);\r\n }\r\n}</code></pre>\r\n<pre class=\"language-html\"><code>package com.ctg.test.cros.springbootcros.controller;\r\n \r\n \r\nimport com.ctg.test.cros.springbootcros.service.UserService;\r\nimport com.ctg.test.cros.springbootcros.util.CookieUtils;\r\nimport com.ctg.test.cros.springbootcros.util.IpUtil;\r\nimport com.ctg.test.cros.springbootcros.util.ResultUtil;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\r\nimport org.springframework.core.env.Environment;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.bind.annotation.RequestMethod;\r\nimport org.springframework.web.bind.annotation.RequestParam;\r\nimport org.springframework.web.bind.annotation.ResponseBody;\r\n \r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport javax.servlet.http.HttpSession;\r\nimport java.net.InetAddress;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n \r\n/**\r\n * 启动2个后台进程,\r\n * server.port=8080\r\n * server.context-path=/webapp1\r\n * server.port=8090\r\n * server.context-path=/webapp2\r\n * 访问:http://localhost:8080/webapp1\r\n */\r\n@Controller\r\n@EnableAutoConfiguration\r\npublic class IndexController {\r\n @Autowired\r\n private Environment env;\r\n @Autowired\r\n IpUtil ipUtil;\r\n \r\n @Autowired\r\n private UserService userService;\r\n @RequestMapping(\"/\")\r\n String index(HttpServletRequest request) {\r\n return \"/index\";\r\n }\r\n @RequestMapping(\"/index\")\r\n String index2(HttpServletRequest request) {\r\n return \"/index\";\r\n }\r\n @RequestMapping(\"/test\")\r\n @ResponseBody\r\n public Object test(HttpServletRequest request, HttpServletResponse response) {\r\n Map&lt;String,Object&gt; result=new HashMap&lt;&gt;();\r\n String ip=\"\";\r\n try {\r\n HttpSession session = request.getSession();\r\n String id = session.getId();\r\n InetAddress inetAddress=ipUtil.getLocalHostLANAddress();\r\n ip=inetAddress.getHostAddress();\r\n } catch (Exception e) {\r\n e.printStackTrace();\r\n }\r\n result.put(\"data\",\"url:\"+request.getRequestURL()+\",ip: \"+ip+\",port:\"+env.getProperty(\"server.port\"));\r\n return ResultUtil.success(result);\r\n }\r\n @RequestMapping(value = \"/setSession\", method ={RequestMethod.POST,RequestMethod.GET} )\r\n @ResponseBody\r\n public Object setSession(HttpSession session, HttpServletRequest request, HttpServletResponse response, @RequestParam(\"key\") String key\r\n , @RequestParam(\"value\") String value) {\r\n Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();\r\n request.getSession().setAttribute(key, value);\r\n result.put(\"setSession-session_id\", session.getId());\r\n System.out.println(\"放入的sessionID\"+session.getId());\r\n List list = userService.query();\r\n request.getSession().setAttribute(\"list\",list);\r\n result.put(\"session_key\", key);\r\n result.put(\"session_value\", value);\r\n CookieUtils.writeCookie(response,key,value);\r\n return ResultUtil.success(result);\r\n }\r\n \r\n @RequestMapping(value = \"/getSession\", method = RequestMethod.GET)\r\n @ResponseBody\r\n public Object getSession(HttpSession session, HttpServletRequest request, HttpServletResponse response, @RequestParam(\"key\") String key) {\r\n Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();\r\n System.out.println(\"获取到的sessionID\"+session.getId());\r\n result.put(\"getSession-session_id\", session.getId());\r\n result.put(\"session_key\", key);\r\n result.put(\"cookie获取session_value\", CookieUtils.getCookie(request,key));\r\n return ResultUtil.success(result);\r\n }\r\n \r\n @RequestMapping(value = \"/query\", method = RequestMethod.POST)\r\n @ResponseBody\r\n public Object query(HttpSession session){\r\n List list = (List)session.getAttribute(\"list\");\r\n return ResultUtil.success(list);\r\n }\r\n}</code></pre>\r\n<pre class=\"language-html\"><code>#端口和上下文\r\nserver.port=8090\r\nserver.context-path=/webapp2\r\n##html##\r\nspring.thymeleaf.cache=false\r\nspring.thymeleaf.check-template-location=true\r\nspring.thymeleaf.content-type=text/html\r\nspring.thymeleaf.enabled=true\r\nspring.thymeleaf.mode=LEGACYHTML5\r\nspring.mvc.static-path-pattern=/static\r\nspring.datasource.driver-class-name=com.mysql.jdbc.Driver\r\nspring.datasource.username=root\r\nspring.datasource.password=root\r\nspring.datasource.url=jdbc:mysql://192.168.0.167:3306/test?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false\r\nspring.datasource.type=com.alibaba.druid.pool.DruidDataSource</code></pre>\r\n<p>前端ajax&nbsp;&nbsp;xhrFields: {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;withCredentials: true&nbsp; 这个是携带 cookie的必须携带<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;},</p>\r\n<pre class=\"language-html\"><code>&lt;input type=\"button\" value=\"测试跨域放入session\" onclick=\"setsession();\"/&gt;\r\n &lt;input type=\"button\" value=\"测试跨域获取session\" onclick=\"getsession();\"/&gt;\r\n &lt;input type=\"button\" value=\"获取list\" onclick=\"query();\"/&gt;\r\n \r\n&lt;script&gt;\r\n \r\nfunction setsession(){\r\n $.ajax({\r\n url:\"http://192.168.0.167:8090/webapp2/setSession\",\r\n dataType: \"json\",\r\n type:\"get\",\r\n data:{\r\n value: \'admin\',\r\n key:\"123456\"\r\n },\r\n xhrFields: {\r\n withCredentials: true\r\n },\r\n success:function (res) {\r\n alert(res.msg);\r\n },\r\n error:function(err){\r\n alert(err);\r\n }\r\n });\r\n }\r\n \r\n \r\nfunction getsession(){\r\n $.ajax({\r\n url:\"http://192.168.0.167:8090/webapp2/getSession\",\r\n dataType: \"json\",\r\n type:\"get\",\r\n data:{\r\n value: \'admin\',\r\n key:\"123456\"\r\n },\r\n xhrFields: {\r\n withCredentials: true\r\n },\r\n success:function (res) {\r\n alert(res.msg);\r\n },\r\n error:function(err){\r\n alert(err);\r\n }\r\n });\r\n }\r\n \r\nfunction query(){\r\n $.ajax({\r\n url:\"http://192.168.0.167:8090/webapp2/query\",\r\n dataType: \"json\",\r\n type:\"post\",\r\n xhrFields: {\r\n withCredentials: true\r\n },\r\n success:function (res) {\r\n alert(res.msg);\r\n },\r\n error:function(err){\r\n alert(err);\r\n }\r\n });\r\n }\r\n&lt;/script&gt;</code></pre>\r\n<p>加群:687942640</p>\r\n</body>\r\n</html>','tinymce'),(13,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>1.maven依赖</p>\r\n<pre class=\"language-html\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;commons-io&lt;/groupId&gt;\r\n &lt;artifactId&gt;commons-io&lt;/artifactId&gt;\r\n &lt;version&gt;1.3.2&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;\r\n &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;\r\n &lt;version&gt;1.2.1&lt;/version&gt;\r\n &lt;/dependency&gt;</code></pre>\r\n<p>2.设置上传文件大小,并且放置在启动类中</p>\r\n<pre class=\"has\"><code class=\"language-java hljs\"></code></pre>\r\n<ol class=\"hljs-ln\">\r\n<li>\r\n<div class=\"hljs-ln-numbers\">&nbsp;</div>\r\n</li>\r\n</ol>\r\n<pre class=\"has\"><code class=\"language-java hljs\"></code></pre>\r\n<pre class=\"language-html\"><code>package com.example;\r\n \r\nimport org.mybatis.spring.annotation.MapperScan;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\r\nimport org.springframework.boot.builder.SpringApplicationBuilder;\r\nimport org.springframework.boot.web.servlet.MultipartConfigFactory;\r\nimport org.springframework.boot.web.servlet.support.SpringBootServletInitializer;\r\nimport org.springframework.context.annotation.Bean;\r\n \r\nimport javax.servlet.MultipartConfigElement;\r\n \r\n@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)\r\n@MapperScan(basePackages = {\"com.example.mapper\"})\r\npublic class TracingApplication extends SpringBootServletInitializer {\r\n \r\n public static void main(String[] args) {\r\n SpringApplication.run(TracingApplication.class, args);\r\n }\r\n \r\n @Override\r\n protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {\r\n return builder.sources(TracingApplication.class);\r\n }\r\n \r\n @Bean\r\n public MultipartConfigElement multipartConfigElement() {\r\n MultipartConfigFactory factory = new MultipartConfigFactory();\r\n //单个文件最大\r\n factory.setMaxFileSize(\"100MB\"); //KB,MB\r\n /// 设置总上传数据总大小\r\n factory.setMaxRequestSize(\"100MB\");\r\n return factory.createMultipartConfig();\r\n }\r\n}\r\n </code></pre>\r\n<pre class=\"language-html\"><code>server:\r\n port: 8585\r\n \r\nspring:\r\n datasource:\r\n driver-class-name: com.mysql.jdbc.Driver\r\n username: root\r\n password: root\r\n url: jdbc:mysql://192.168.0.167:3306/shiro?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false\r\n \r\nmybatis-plus:\r\n mapper-locations: classpath:/mapping/*Mapper.xml\r\n typeAliasesPackage: com.example.modle\r\n global-config:\r\n db-config:\r\n id-type: auto\r\n field-strategy: IGNORED\r\n column-underline: true\r\n #capital-mode: true\r\n logic-delete-value: 1\r\n logic-not-delete-value: 0\r\n sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector\r\n db-type: mysql\r\n refresh: true\r\n #meta-object-handler: com.baomidou.springboot.xxx\r\n #sql-injector: com.baomidou.springboot.xxx\r\n configuration:\r\n map-underscore-to-camel-case: true\r\n cache-enabled: false\r\n# 打印sql\r\nlogging:\r\n level:\r\n com.example.mapper : debug\r\nfile:\r\n paths: /usr/tmp/\r\n #paths: d:\\\\var\\\\ 此处是windows 上传路径</code></pre>\r\n<pre class=\"language-html\"><code>package com.example.controller;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/1/23\r\n * Time:14:17\r\n */\r\n \r\nimport com.example.common.R;\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.web.bind.annotation.*;\r\nimport org.springframework.web.multipart.MultipartFile;\r\nimport org.springframework.web.multipart.MultipartHttpServletRequest;\r\nimport org.springframework.web.multipart.commons.CommonsMultipartResolver;\r\n \r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.io.*;\r\nimport java.util.Iterator;\r\nimport java.util.List;\r\nimport java.util.UUID;\r\n \r\n/**\r\n * 文件上传\r\n */\r\n@Controller\r\npublic class FileController {\r\n \r\n \r\n @Value(\"${file.paths}\")\r\n private String path;\r\n \r\n \r\n // 判断文件夹是否存在\r\n public static void judeDirExists(File file) {\r\n \r\n if (file.exists()) {\r\n if (file.isDirectory()) {\r\n System.out.println(\"dir exists\");\r\n } else {\r\n System.out.println(\"the same name file exists, can not create dir\");\r\n }\r\n } else {\r\n System.out.println(\"dir not exists, create it ...\");\r\n file.mkdir();\r\n }\r\n \r\n }\r\n \r\n \r\n // 上传\r\n @RequestMapping(value = \"/fileUpload\", method = RequestMethod.POST)\r\n @ResponseBody\r\n public R testUpload(HttpServletRequest request){\r\n CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());\r\n if(commonsMultipartResolver.isMultipart(request)){\r\n MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;\r\n List&lt;MultipartFile&gt; list = multipartHttpServletRequest.getFiles(\"files\");\r\n for(MultipartFile multipartFile : list){\r\n String fileName = multipartFile.getOriginalFilename();\r\n //判断上传文件类型\r\n String type = fileName.indexOf(\".\") != -1 ? fileName.substring(fileName.lastIndexOf(\".\") ,fileName.length()):null;\r\n if(type.equals(\".jpg\")||type.equals(\".png\")||type.equals(\"jpeg\")){\r\n //获取后缀\r\n String sub = fileName.substring(fileName.lastIndexOf(\".\"));\r\n //uuid确保不重复\r\n String paths = path+UUID.randomUUID()+sub;\r\n File file = new File(path);\r\n if (!file.exists()) {\r\n file.mkdirs();//是否存在文件夹\r\n }\r\n file = new File(paths);\r\n try {\r\n InputStream inputStream = multipartFile.getInputStream();\r\n OutputStream outputStream = new FileOutputStream(file);\r\n byte[] bs = new byte[1024];\r\n int res = 0;\r\n while ((res=inputStream.read(bs))!=-1){\r\n outputStream.write(bs,0,res);\r\n }\r\n System.out.println(\"ok\");\r\n } catch (IOException e) {\r\n e.printStackTrace();\r\n return R.error(\"失败\");\r\n }\r\n \r\n }else {\r\n R.error(\"文件格式错误\");\r\n }\r\n }\r\n \r\n }\r\n return R.ok(\"上传成功\");\r\n }\r\n \r\n \r\n}</code></pre>\r\n<pre class=\"language-html\"><code>&lt;input type=\"file\" id=\"qw\" multiple=\"files\" accept=\"image/jpg,image/jpeg\" name=\"qw\" onchange=\"ajaxUpload();\"/&gt;\r\n \r\n \r\n \r\nfunction ajaxUpload() {\r\n var fd = new FormData();\r\n var pf = $(\"#qw\").prop(\"files\");\r\n for(var i=0;i&lt;$(\'#qw\')[0].files.length;i++){\r\n fd.append(\'files\',$(\'#qw\')[0].files[i]);\r\n }\r\n $.ajax({\r\n type : \"post\",\r\n url : url,\r\n async : false,\r\n processData : false, // 不处理数据\r\n contentType : false, // 不设置内容类型\r\n data : fd,\r\n headers: {\r\n \"token\": token //token携带\r\n },\r\n dataType : \'json\', //返回类型json、text\r\n success : function(data) {\r\n alert(data.msg);\r\n }\r\n });\r\n }</code></pre>\r\n<p>Tomcat 服务端部署时最好对路径进行映射</p>\r\n<pre class=\"language-html\"><code>在server.xml 设置路径映射\r\n \r\n&lt;Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs\"\r\n prefix=\"localhost_access_log\" suffix=\".txt\"\r\n pattern=\"%h %l %u %t &amp;quot;%r&amp;quot; %s %b\" /&gt;\r\n \r\n#我此处设置的是linux路径\r\n &lt;Context path=\"/photo\" docBase=\"/usr/tmp/\" reloadable=\"true\" /&gt;</code></pre>\r\n</body>\r\n</html>','tinymce'),(14,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<pre class=\"language-html\"><code>&lt;dependency&gt;\r\n &lt;groupId&gt;com.auth0&lt;/groupId&gt;\r\n &lt;artifactId&gt;java-jwt&lt;/artifactId&gt;\r\n &lt;version&gt;2.2.0&lt;/version&gt;\r\n&lt;/dependency&gt;</code></pre>\r\n<pre class=\"language-html\"><code>public class JwtUtils {\r\n \r\n /**\r\n * 密钥\r\n */\r\n private static final String SECRET=\"xxxx\";\r\n /**\r\n * 默认字段key:exp\r\n */\r\n private static final String EXP=\"exp\";\r\n /**\r\n * 默认字段key:payload\r\n */\r\n private static final String PAYLOAD=\"payload\";\r\n \r\n /**\r\n * 加密\r\n * @param object 加密数据\r\n * @param maxTime 有效期(毫秒数)\r\n * @param &lt;T&gt;\r\n * @return\r\n */\r\n public static &lt;T&gt; String encode(T object,long maxTime){\r\n try{\r\n final JWTSigner signer=new JWTSigner(SECRET);\r\n final Map&lt;String ,Object&gt; data=new HashMap&lt;&gt;(10);\r\n ObjectMapper objectMapper=new ObjectMapper();\r\n String jsonString=objectMapper.writeValueAsString(object);\r\n data.put(PAYLOAD,jsonString);\r\n data.put(EXP,System.currentTimeMillis()+maxTime);\r\n return signer.sign(data);\r\n } catch (IOException e) {\r\n e.printStackTrace();\r\n return null;\r\n }\r\n }\r\n \r\n /**\r\n * 数据解密\r\n * @param jwt 解密数据\r\n * @param tClass 解密类型\r\n * @param &lt;T&gt;\r\n * @return\r\n */\r\n public static &lt;T&gt; T decode(String jwt,Class&lt;T&gt; tClass) {\r\n final JWTVerifier jwtVerifier = new JWTVerifier(SECRET);\r\n try {\r\n final Map&lt;String, Object&gt; data = jwtVerifier.verify(jwt);\r\n //判断数据是否超时或者符合标准\r\n if (data.containsKey(EXP) &amp;&amp; data.containsKey(PAYLOAD)) {\r\n long exp = (long) data.get(EXP);\r\n long currentTimeMillis = System.currentTimeMillis();\r\n if (exp &gt; currentTimeMillis) {\r\n String json = (String) data.get(PAYLOAD);\r\n ObjectMapper objectMapper = new ObjectMapper();\r\n return objectMapper.readValue(json, tClass);\r\n }\r\n }\r\n return null;\r\n } catch (Exception e) {\r\n //e.printStackTrace();\r\n return null;\r\n }\r\n }\r\n \r\n //解密token取出userId\r\n public static &lt;T&gt; T updateDecode(String jwt,Class&lt;T&gt; tClass){\r\n final JWTVerifier jwtVerifier = new JWTVerifier(SECRET);\r\n try {\r\n final Map&lt;String, Object&gt; data = jwtVerifier.verify(jwt);\r\n String json = (String) data.get(PAYLOAD);\r\n ObjectMapper objectMapper = new ObjectMapper();\r\n return objectMapper.readValue(json, tClass);\r\n } catch (Exception e) {\r\n //e.printStackTrace();\r\n return null;\r\n }\r\n }\r\n public static void main(String[] args) throws InterruptedException {\r\n// 有效期10秒\r\n// 加密:\r\n// TestCenterAdministratorsVO test = new TestCenterAdministratorsVO();\r\n// test.setId(1L);\r\n// test.setLoginName(\"sa\");\r\n// String token=encode(test,1000000);\r\n// System.out.println(\"TOKEN=======\"+token);\r\n// //Thread.sleep(10000);\r\n//// 解密\r\n// TestCenterAdministratorsVO user=decode(token,TestCenterAdministratorsVO.class);\r\n// System.out.println(user.getId()+user.getLoginName());\r\n //removeDecode(\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDcwODkwNTYxNDgsInBheWxvYWQiOiJ7XCJpZFwiOjEsXCJuYW1lXCI6bnVsbCxcInBob25lTnVtYmVyXCI6bnVsbCxcImxvZ2luTmFtZVwiOlwic2FcIixcInNlcmlhbE51bWJlclwiOm51bGwsXCJ0eXBlXCI6bnVsbCxcInRlc3RDZW50ZXJJZFwiOm51bGwsXCJvcmdhbml6YXRpb25JZFwiOm51bGwsXCJtYWluU2l0ZUFkbWluXCI6ZmFsc2UsXCJhZG1pblwiOmZhbHNlLFwib3JnYW5pemF0aW9uTmFtZVwiOm51bGx9In0.FgYm4wSDhkZukqlRukjwvxQ1BM746AWQfCmGucMP3pc\");\r\n updateDecode(\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDcwMTgxNTU4OTYsInBheWxvYWQiOiJ7XCJpZFwiOjIwMTAwMDAzMyxcIm5hbWVcIjpcIuW6numTtuaYjFwiLFwicGhvbmVOdW1iZXJcIjpudWxsLFwibG9naW5OYW1lXCI6XCJwYW5neWluY2hhbmdcIixcInNlcmlhbE51bWJlclwiOm51bGwsXCJ0eXBlXCI6XCJNQUlOU0lURUFETUlOXCIsXCJ0ZXN0Q2VudGVySWRcIjo3OSxcIm9yZ2FuaXphdGlvbklkXCI6XCIwMDFcIixcIm1haW5TaXRlQWRtaW5cIjp0cnVlLFwiYWRtaW5cIjpmYWxzZSxcIm9yZ2FuaXphdGlvbk5hbWVcIjpudWxsfSJ9.yrYlU4djPPQyu1mneQgkVgLCEQiJ2pKkyX8EVw0NcY8\",TestCenterAdministratorsVO.class);\r\n \r\n }\r\n}</code></pre>\r\n<pre class=\"language-html\"><code>import com.alibaba.fastjson.JSONObject;\r\nimport org.springframework.web.bind.annotation.RequestMethod;\r\n \r\nimport java.io.BufferedReader;\r\nimport java.io.IOException;\r\nimport java.io.InputStreamReader;\r\n \r\nimport javax.servlet.Filter;\r\nimport javax.servlet.FilterChain;\r\nimport javax.servlet.FilterConfig;\r\nimport javax.servlet.ServletException;\r\nimport javax.servlet.ServletRequest;\r\nimport javax.servlet.ServletResponse;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/1/8\r\n * Time:16:15\r\n */\r\npublic class AjaxFilter implements Filter {\r\n \r\n @Override\r\n public void destroy() {\r\n \r\n }\r\n \r\n \r\n @Override\r\n public void doFilter(ServletRequest req, ServletResponse res,\r\n FilterChain chain) throws IOException, ServletException {\r\n HttpServletRequest request = (HttpServletRequest) req;\r\n HttpServletResponse response = (HttpServletResponse) res;\r\n //禁止首页访问\r\n String url = request.getRequestURI();\r\n if(\"/\".equals(url)){\r\n return;\r\n }\r\n // 指定允许其他域名访问\r\n response.setHeader(\"Access-Control-Allow-Origin\", \"*\");\r\n // 响应类型\r\n response.setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, DELETE, OPTIONS, DELETE\");\r\n // 响应头设置\r\n response.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, x-requested-with, X-Custom-Header, HaiYi-Access-Token\");\r\n chain.doFilter(req, res);\r\n }\r\n \r\n @Override\r\n public void init(FilterConfig arg0) throws ServletException {\r\n }\r\n}</code></pre>\r\n<pre class=\"language-html\"><code>&lt;filter&gt;\r\n &lt;filter-name&gt;ajaxFilter&lt;/filter-name&gt;\r\n &lt;filter-class&gt;*.*.*.web.servlet.barcode.AjaxFilter&lt;/filter-class&gt;\r\n &lt;/filter&gt;\r\n &lt;filter-mapping&gt;\r\n &lt;filter-name&gt;ajaxFilter&lt;/filter-name&gt;\r\n &lt;url-pattern&gt;/*&lt;/url-pattern&gt;\r\n &lt;/filter-mapping&gt;</code></pre>\r\n<pre class=\"language-html\"><code>response .setContentType(\"text/html;charset=utf-8\");\r\n PrintWriter out = response.getWriter();\r\n String actionName = context.getActionInvocation().getProxy().getActionName();\r\n String url = request.getRequestURI();\r\n ServletContext application = ServletActionContext.getServletContext();\r\n WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);\r\n this.testCenterService = (TestCenterService)ctx.getBean(\"testCenterService\");\r\n if(!url.equals(\"/login\")){\r\n //悠闲判断是否为空或者时间是否失效\r\n String token = request.getParameter(\"token\");\r\n TestCenterAdministratorsVO user = JwtUtils.decode(token, TestCenterAdministratorsVO.class);\r\n if(null==token||\"\".equals(token)||null==user){\r\n out.print(Message.error(0,\"token不合法!\"));\r\n return null;\r\n }else{\r\n try {\r\n //再次判断数据库状态\r\n TestCenterAdministratorsVO users = JwtUtils.updateDecode(token, TestCenterAdministratorsVO.class);\r\n String tokens = testCenterService.scheckToken(token,users.getId());\r\n if(null==tokens){\r\n out.print(Message.error(0,\"token不合法!\"));\r\n return null;\r\n }\r\n }catch (Exception e){\r\n out.print(Message.error());\r\n return null;\r\n }\r\n }\r\n }</code></pre>\r\n<p>加群:687942640</p>\r\n</body>\r\n</html>','tinymce'),(15,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>安装ActiveMq 下载地址:<a href=\"http://activemq.apache.org/activemq-5140-release.html\" target=\"_blank\" rel=\"nofollow noopener\">http://activemq.apache.org/activemq-5140-release.html</a></p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190107140449252.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/2019010714122692.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n<p><a href=\"http://127.0.0.1:8161/admin/\" target=\"_blank\" rel=\"nofollow noopener\">http://127.0.0.1:8161/admin/</a>&nbsp;为访问地址:账户默认为:admin 密码:admin</p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190107141110617.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n<pre class=\"language-html\"><code>yml配置\r\n \r\nspring:\r\n activemq:\r\n broker-url: tcp://localhost:61616\r\n in-memory: true\r\n pool:\r\n enabled: false\r\n password: admin\r\n user: admin\r\n \r\nmaven依赖\r\n \r\n&lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.activemq&lt;/groupId&gt;\r\n &lt;artifactId&gt;activemq-pool&lt;/artifactId&gt;\r\n &lt;version&gt;5.14.5&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n &lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.activemq&lt;/groupId&gt;\r\n &lt;artifactId&gt;activemq-all&lt;/artifactId&gt;\r\n &lt;version&gt;5.15.5&lt;/version&gt;\r\n &lt;/dependency&gt;\r\n \r\n&lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-activemq&lt;/artifactId&gt;\r\n &lt;/dependency&gt;</code></pre>\r\n<p>生产者</p>\r\n<pre class=\"language-html\"><code>package com.ocean.service;\r\n \r\nimport org.apache.activemq.ScheduledMessage;\r\nimport org.apache.activemq.command.ActiveMQQueue;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.jms.core.JmsMessagingTemplate;\r\nimport org.springframework.stereotype.Service;\r\n \r\nimport javax.jms.*;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/1/7\r\n * Time:11:16\r\n */\r\n@Service(\"producer\")\r\npublic class Producer {\r\n @Autowired // 也可以注入JmsTemplate,JmsMessagingTemplate对JmsTemplate进行了封装\r\n private JmsMessagingTemplate jmsTemplate;\r\n \r\n \r\n // 发送消息,destination是发送到的队列,message是待发送的消息\r\n public void sendMessage( final String message){\r\n Destination destination = new ActiveMQQueue(\"mytest.queue\");\r\n jmsTemplate.convertAndSend(destination, message);\r\n }\r\n \r\n /**\r\n * @desc 延时发送\r\n */\r\n public void delaySend(String text, String queueName, Long time) {\r\n //获取连接工厂\r\n ConnectionFactory connectionFactory = this.jmsTemplate.getConnectionFactory();\r\n try {\r\n //获取连接\r\n Connection connection = connectionFactory.createConnection();\r\n connection.start();\r\n //获取session\r\n Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);\r\n // 创建一个消息队列\r\n Destination destination = session.createQueue(queueName);\r\n MessageProducer producer = session.createProducer(destination);\r\n producer.setDeliveryMode(DeliveryMode.PERSISTENT);\r\n TextMessage message = session.createTextMessage(text);\r\n //设置延迟时间\r\n message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time);\r\n //发送\r\n producer.send(message);\r\n session.commit();\r\n producer.close();\r\n session.close();\r\n connection.close();\r\n } catch (Exception e) {\r\n e.getMessage();\r\n }\r\n }\r\n}</code></pre>\r\n<p>消费者:</p>\r\n<pre class=\"language-html\"><code>package com.ocean.config;\r\n \r\nimport org.springframework.jms.annotation.JmsListener;\r\nimport org.springframework.messaging.handler.annotation.SendTo;\r\nimport org.springframework.stereotype.Component;\r\n \r\nimport java.util.Date;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/1/7\r\n * Time:11:17\r\n */\r\n@Component\r\npublic class Consumer {\r\n \r\n // 使用JmsListener配置消费者监听的队列,其中text是接收到的消息\r\n @JmsListener(destination = \"mytest.queue\")\r\n public void receiveQueue(String text) throws InterruptedException {\r\n Thread.sleep(5000);\r\n System.out.println(\"consumer接收到\"+text+\"的请求并处理完毕,时间是\"+new Date());\r\n }\r\n \r\n \r\n}</code></pre>\r\n<p>测试:</p>\r\n<pre class=\"language-html\"><code> \r\n@Controller\r\npublic class UserController {\r\n@Autowired\r\n private Producer producer;\r\n \r\n/**\r\n * 消息队列实现方式\r\n */\r\n @RequestMapping(\"/queue\")\r\n @ResponseBody\r\n public String queue(){\r\n \r\n for (int i = 0; i &lt; 10 ; i++){\r\n producer.sendMessage( \"queue\"+i);\r\n }\r\n \r\n return \"queue 发送成功\";\r\n }\r\n \r\n}</code></pre>\r\n<p>延迟5秒是为了模仿业务 处理以下为消费后的信息</p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190107140808726.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTk2MzI3,size_16,color_FFFFFF,t_70\" /></p>\r\n</body>\r\n</html>','tinymce'),(16,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<p>结合上一篇点对点(queue),本篇为Topic</p>\r\n<p>yml修改一个配置:</p>\r\n<pre class=\"language-html\"><code>spring:\r\n \r\n#默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置\r\n jms:\r\n pub-sub-domain: true</code></pre>\r\n<pre class=\"language-html\"><code>package com.ocean.service;\r\n \r\nimport org.apache.activemq.ScheduledMessage;\r\nimport org.apache.activemq.command.ActiveMQQueue;\r\nimport org.apache.activemq.command.ActiveMQTopic;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.jms.core.JmsMessagingTemplate;\r\nimport org.springframework.stereotype.Service;\r\n \r\nimport javax.jms.*;\r\n \r\n/**\r\n * Created with IDEA\r\n * author:QinWei\r\n * Date:2019/1/7\r\n * Time:11:16\r\n */\r\n@Service(\"producer\")\r\npublic class Producer {\r\n @Autowired // 也可以注入JmsTemplate,JmsMessagingTemplate对JmsTemplate进行了封装\r\n private JmsMessagingTemplate jmsTemplate;\r\n \r\n \r\n // 发送消息,destination是发送到的队列,message是待发送的消息\r\n public void sendMessage( final String message){\r\n //点对点模式\r\n //Destination destination = new ActiveMQQueue(\"mytest.queue\");\r\n \r\n //Topic 订阅模式\r\n Destination destinations = new ActiveMQTopic(\"active.topic\");\r\n jmsTemplate.convertAndSend(destinations, message);\r\n }\r\n \r\n /**\r\n * @desc 延时发送\r\n */\r\n public void delaySend(String text, String queueName, Long time) {\r\n //获取连接工厂\r\n ConnectionFactory connectionFactory = this.jmsTemplate.getConnectionFactory();\r\n try {\r\n //获取连接\r\n Connection connection = connectionFactory.createConnection();\r\n connection.start();\r\n //获取session\r\n Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);\r\n // 创建一个消息队列\r\n Destination destination = session.createQueue(queueName);\r\n MessageProducer producer = session.createProducer(destination);\r\n producer.setDeliveryMode(DeliveryMode.PERSISTENT);\r\n TextMessage message = session.createTextMessage(text);\r\n //设置延迟时间\r\n message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time);\r\n //发送\r\n producer.send(message);\r\n session.commit();\r\n producer.close();\r\n session.close();\r\n connection.close();\r\n } catch (Exception e) {\r\n e.getMessage();\r\n }\r\n }\r\n}</code></pre>\r\n<p>&nbsp;可以看到订阅者都可以接收到消息</p>\r\n<p><img src=\"https://img-blog.csdnimg.cn/20190107152557104.png\" /></p>\r\n</body>\r\n</html>','tinymce'),(17,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>在项目开发中,对于异常处理我们通常有多种处理方式,比如:控制层手动捕获异常,拦截器统一处理异常。今天跟大家分享一种注解的方式,统一拦截异常并处理。</p>\r\n<h2>异常处理</h2>\r\n<p>在spring 3.2中,新增了@RestControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。</p>\r\n<p>创建 RRExceptionHandler,并添加 @RestControllerAdvice注解,来这样就可以拦截所有控制层上抛出来的异常。</p>\r\n<pre class=\"language-html\"><code>/**\r\n * 异常处理器\r\n * 创建时间 2017年11月20日\r\n */\r\n@RestControllerAdvice\r\npublic class RRExceptionHandler {\r\n\r\n @Autowired\r\n private IMailService mailService;\r\n\r\n private Logger logger = LoggerFactory.getLogger(getClass());\r\n\r\n @Value(\"${alarm.email}\")\r\n private String[] email;\r\n\r\n /**\r\n * 自定义异常\r\n */\r\n @ExceptionHandler(RRException.class)\r\n public Result handleRRException(RRException e){\r\n Result r = new Result();\r\n r.put(\"code\", e.getCode());\r\n r.put(\"msg\", e.getMessage());\r\n return r;\r\n }\r\n\r\n @ExceptionHandler(DuplicateKeyException.class)\r\n public Result handleDuplicateKeyException(DuplicateKeyException e){\r\n logger.error(e.getMessage(), e);\r\n return Result.error(\"数据库中已存在该记录\");\r\n }\r\n\r\n @ExceptionHandler(Exception.class)\r\n public Result handleException(Exception e){\r\n StringWriter stringWriter = new StringWriter();\r\n e.printStackTrace(new PrintWriter(stringWriter));\r\n Email mail = new Email();\r\n mail.setEmail(email);\r\n mail.setSubject(\"工作流系统告警\");\r\n mail.setContent(stringWriter.toString());\r\n //mailService.send(mail);\r\n mailService.sendFreemarker(mail);\r\n logger.error(e.getMessage(), e);\r\n return Result.error();\r\n }\r\n}</code></pre>\r\n<p>自定义异常 RRException:</p>\r\n<pre class=\"language-html\"><code>/**\r\n * 自定义异常\r\n * 创建时间 2017年11月20日\r\n */\r\npublic class RRException extends RuntimeException {\r\n \r\n private static final long serialVersionUID = 1L;\r\n \r\n private String msg;\r\n \r\n private int code = 500;\r\n \r\n public RRException(String msg) {\r\n super(msg);\r\n this.msg = msg;\r\n }\r\n \r\n public RRException(String msg, Throwable e) {\r\n super(msg, e);\r\n this.msg = msg;\r\n }\r\n \r\n public RRException(String msg, int code) {\r\n super(msg);\r\n this.msg = msg;\r\n this.code = code;\r\n }\r\n \r\n public RRException(String msg, int code, Throwable e) {\r\n super(msg, e);\r\n this.msg = msg;\r\n this.code = code;\r\n }\r\n\r\n public String getMsg() {\r\n return msg;\r\n }\r\n\r\n public void setMsg(String msg) {\r\n this.msg = msg;\r\n }\r\n\r\n public int getCode() {\r\n return code;\r\n }\r\n\r\n public void setCode(int code) {\r\n this.code = code;\r\n }\r\n}</code></pre>\r\n<h2>邮件通知</h2>\r\n<p>邮件通知,需要引入以下配置:</p>\r\n<pre class=\"language-html\"><code>&lt;!-- email --&gt;\r\n&lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-mail&lt;/artifactId&gt;\r\n&lt;/dependency&gt;\r\n&lt;!-- freemarker 模版 --&gt;\r\n&lt;dependency&gt;\r\n &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;spring-boot-starter-freemarker&lt;/artifactId&gt;\r\n&lt;/dependency&gt;</code></pre>\r\n<pre class=\"language-html\"><code># freemarker\r\nspring.freemarker.template-loader-path=classpath:/templates/\r\nspring.freemarker.suffix=.ftl\r\nspring.freemarker.enabled=true\r\nspring.freemarker.cache=false\r\nspring.freemarker.charset=UTF-8\r\nspring.freemarker.content-type=text/html\r\nspring.freemarker.allow-request-override=false\r\nspring.freemarker.check-template-location=true\r\nspring.freemarker.expose-request-attributes=false\r\nspring.freemarker.expose-session-attributes=false\r\nspring.freemarker.expose-spring-macro-helpers=false\r\n\r\n# 邮件配置\r\nspring.mail.host=smtp.163.com\r\nspring.mail.username=13105423559@163.com\r\nspring.mail.password=123456\r\nspring.mail.properties.mail.smtp.auth=true\r\nspring.mail.properties.mail.smtp.starttls.enable=true\r\nspring.mail.properties.mail.smtp.starttls.required=true\r\n\r\n# 告警通知 多个以逗号分隔\r\nalarm.email = 345849402@qq.com</code></pre>\r\n<p>定义Email封装类:</p>\r\n<pre class=\"language-html\"><code>/**\r\n * Email封装类\r\n */\r\npublic class Email implements Serializable {\r\n private static final long serialVersionUID = 1L;\r\n // 必填参数\r\n private String[] email;// 接收方邮件\r\n private String subject;// 主题\r\n private String content;// 邮件内容\r\n // 选填\r\n private String template;// 模板\r\n private HashMap&lt;String, String&gt; kvMap;// 自定义参数\r\n\r\n public Email() {\r\n super();\r\n }\r\n\r\n public Email(String[] email, String subject, String content, String template, HashMap&lt;String, String&gt; kvMap) {\r\n super();\r\n this.email = email;\r\n this.subject = subject;\r\n this.content = content;\r\n this.template = template;\r\n this.kvMap = kvMap;\r\n }\r\n\r\n public String[] getEmail() {\r\n return email;\r\n }\r\n\r\n public void setEmail(String[] email) {\r\n this.email = email;\r\n }\r\n\r\n public String getSubject() {\r\n return subject;\r\n }\r\n\r\n public void setSubject(String subject) {\r\n this.subject = subject;\r\n }\r\n\r\n public String getContent() {\r\n return content;\r\n }\r\n\r\n public void setContent(String content) {\r\n this.content = content;\r\n }\r\n\r\n public String getTemplate() {\r\n return template;\r\n }\r\n\r\n public void setTemplate(String template) {\r\n this.template = template;\r\n }\r\n\r\n public HashMap&lt;String, String&gt; getKvMap() {\r\n return kvMap;\r\n }\r\n\r\n public void setKvMap(HashMap&lt;String, String&gt; kvMap) {\r\n this.kvMap = kvMap;\r\n }\r\n}</code></pre>\r\n<p>发送接口:</p>\r\n<pre class=\"language-html\"><code>public interface IMailService {\r\n /**\r\n * 纯文本\r\n * @param mail\r\n * @throws Exception\r\n */\r\n public void send(Email mail);\r\n /**\r\n * 模版发送 freemarker\r\n * @param mail\r\n * @throws Exception\r\n */\r\n public void sendFreemarker(Email mail);\r\n \r\n}</code></pre>\r\n<p>发送实现:</p>\r\n<pre class=\"language-html\"><code>@Service\r\npublic class MailServiceImpl implements IMailService {\r\n private static final Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);\r\n @Autowired\r\n private JavaMailSender mailSender;//执行者\r\n @Autowired\r\n public Configuration configuration;//freemarker\r\n @Value(\"${spring.mail.username}\")\r\n public String USER_NAME;//发送者\r\n @Value(\"${server.path}\")\r\n public String PATH;//邮件服务地址,用于显示图片\r\n \r\n //文本分割\r\n static {\r\n System.setProperty(\"mail.mime.splitlongparameters\",\"false\");\r\n }\r\n\r\n @Override\r\n public void send(Email mail) {\r\n try {\r\n logger.info(\"发送邮件:{}\",mail.getContent());\r\n SimpleMailMessage message = new SimpleMailMessage();\r\n message.setFrom(USER_NAME);\r\n message.setTo(mail.getEmail());\r\n message.setSubject(mail.getSubject());\r\n message.setText(mail.getContent());\r\n mailSender.send(message);\r\n } catch (Exception e) {\r\n e.printStackTrace();\r\n }\r\n }\r\n @Override\r\n public void sendFreemarker(Email mail) {\r\n try {\r\n MimeMessage message = mailSender.createMimeMessage();\r\n MimeMessageHelper helper = new MimeMessageHelper(message, true);\r\n //这里可以自定义发信名称比如:工作流\r\n helper.setFrom(USER_NAME,\"工作流\");\r\n helper.setTo(mail.getEmail());\r\n helper.setSubject(mail.getSubject());\r\n Map&lt;String, Object&gt; model = new HashMap&lt;String, Object&gt;();\r\n model.put(\"mail\", mail);\r\n model.put(\"path\", PATH);\r\n Template template = configuration.getTemplate(mail.getTemplate());\r\n String text = FreeMarkerTemplateUtils.processTemplateIntoString(\r\n template, model);\r\n helper.setText(text, true);\r\n mailSender.send(message);\r\n } catch (Exception e) {\r\n e.printStackTrace();\r\n }\r\n }\r\n}</code></pre>\r\n<p>定义发送模板 notify.ftl :</p>\r\n<p>.</p>\r\n<pre class=\"language-html\"><code>&lt;!doctype html&gt;\r\n&lt;html lang=\"zh-cmn-Hans\"&gt;\r\n&lt;head&gt;\r\n &lt;meta charset=\"UTF-8\"&gt;\r\n &lt;meta name=\"renderer\" content=\"webkit\" /&gt;\r\n &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" /&gt;\r\n &lt;title&gt;Document&lt;/title&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n 您好:${mail.content} \r\n&lt;/body&gt;\r\n&lt;/html&gt;</code></pre>\r\n</body>\r\n</html>','tinymce'),(18,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>由于业务需要,需要在拦截器中操作Redis缓存,按照 controller,service层配置发现无法注入,一直报空指针异常。</p>\r\n<h2>解决方案</h2>\r\n<pre class=\"language-html\"><code>@Configuration\r\npublic class MyWebAppConfigurer extends WebMvcConfigurerAdapter {\r\n \r\n /**\r\n * 将自定义拦截器作为Bean写入配置\r\n * @return\r\n */\r\n @Bean\r\n public SysInterceptor sysInterceptor() {\r\n return new SysInterceptor();\r\n }\r\n /**\r\n * 对拦截器注册\r\n */\r\n @Override\r\n public void addInterceptors(InterceptorRegistry registry) {\r\n String[] patterns = new String[] {\"/swagger-resources/**\"};\r\n registry.addInterceptor(sysInterceptor())\r\n .addPathPatterns(\"/**\")\r\n .excludePathPatterns(patterns);\r\n super.addInterceptors(registry);\r\n }\r\n}</code></pre>\r\n<p>拦截器:</p>\r\n<pre class=\"language-html\"><code>public class SysInterceptor implements HandlerInterceptor {\r\n \r\n private static final Logger logger = LoggerFactory.getLogger(SysInterceptor.class);\r\n \r\n @Autowired\r\n private RedisTemplate&lt;String, String&gt; redisTemplate;\r\n\r\n \r\n @Override\r\n public boolean preHandle(HttpServletRequest request, HttpServletResponse response,\r\n Object handler) throws Exception {\r\n //业务逻辑\r\n return true;\r\n }\r\n\r\n @Override\r\n public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,\r\n ModelAndView modelAndView) throws Exception {\r\n \r\n }\r\n\r\n @Override\r\n public void afterCompletion(HttpServletRequest request, HttpServletResponse response,\r\n Object handler, Exception ex) throws Exception {\r\n \r\n }\r\n}</code></pre>\r\n<h2>查询原因</h2>\r\n<p>拦截器执行在Bean初始化之前导致这个问题的。在web.xml中各个元素的执行顺序是这样的,context-param--&gt;listener--&gt;filter--&gt;servlet; 而拦截器是在Spring MVC中配置的,如果从整个项目中看,一个servlet请求的执行过程就变成了这样context-param--&gt;listener--&gt;filter--&gt;servlet--&gt;interceptor,为什么拦截器是在servlet执行之后,因为拦截器本身就是在servlet内部的。</p>\r\n<h2>概念</h2>\r\n<ul>\r\n<li>servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。</li>\r\n<li>filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。</li>\r\n<li>listener:监听器,从字面上可以看出listener主要用来监听只用。通过listener可以监听web服务器中某一个执行动作,并根据 其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执 行代码的功能组件。</li>\r\n<li>interceptor:拦截器是对过滤器更加细化的应用,他不仅可以应用在service方法前后还可以应用到其他方法的前后拦截器。</li>\r\n</ul>\r\n</body>\r\n</html>','tinymce'),(19,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>Nginx日志默认情况下写入到一个文件中,为了区分各个域下的日志,我们一般会分开存储。即时这样,文件也会变的越来越大,非常不方便查看分析。通常我们是以每日来做统计的,下面来聊聊以日期来分隔Nginx日志。</p>\r\n<h2>配置</h2>\r\n<h4>编写脚本</h4>\r\n<pre class=\"language-html\"><code>#!/bin/bash\r\n#初始化\r\nLOGS_PATH=/usr/local/nginx/logs\r\nYESTERDAY=$(date -d \"yesterday\" +%Y%m%d)\r\n\r\n#按天切割日志\r\nmv ${LOGS_PATH}/bbs.52itstyle.com.access.log ${LOGS_PATH}/bbs.52itstyle.com.access_${YESTERDAY}.log\r\nmv ${LOGS_PATH}/blog.52itstyle.com.access.log ${LOGS_PATH}/blog.52itstyle.com.access_${YESTERDAY}.log\r\n\r\n#向nginx主进程发送USR1信号,重新打开日志文件,否则会继续往mv后的文件写数据的。原因在于:linux系统中,内核是根据文件描述符来找文件的。如果不这样操作导致日志切割失败。\r\nkill -USR1 `ps axu | grep \"nginx: master process\" | grep -v grep | awk \'{print $2}\'`\r\n\r\n#删除7天前的日志\r\ncd ${LOGS_PATH}\r\nfind . -mtime +7 -name \"*20[1-9][3-9]*\" | xargs rm -f\r\n\r\nexit 0</code></pre>\r\n<h4>写入任务</h4>\r\n<pre class=\"language-html\"><code>#执行命令\r\ncrontab -e\r\n#写入文件并保存\r\n0 0 * * * /home/scripts/cut_del_nginx_logs.sh</code></pre>\r\n<h2>crontab</h2>\r\n<p>crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。Linux下的任务调度分为两类,系统任务调度和用户任务调度。</p>\r\n<p>系统任务调度:系统周期性所要执行的工作,比如写缓存数据到硬盘、日志清理等。在/etc目录下有一个crontab文件,这个就是系统任务调度的配置文件。</p>\r\n<h4>crontab服务安装</h4>\r\n<p>安装crontab:</p>\r\n<pre class=\"language-html\"><code>yum install crontabs</code></pre>\r\n<p>服务操作说明:</p>\r\n<pre class=\"language-html\"><code>service crond start //启动服务\r\nservice crond stop //关闭服务\r\nservice crond restart //重启服务\r\nservice crond reload //重新载入配置</code></pre>\r\n<p>查看crontab服务状态:</p>\r\n<pre class=\"language-html\"><code>service crond status</code></pre>\r\n<p>手动启动crontab服务:</p>\r\n<pre class=\"language-html\"><code>service crond start</code></pre>\r\n<p>查看crontab服务是否已设置为开机启动,执行命令:</p>\r\n<pre class=\"language-html\"><code>ntsysv</code></pre>\r\n<p>加入开机自动启动:</p>\r\n<pre class=\"language-html\"><code>chkconfig &ndash;level 35 crond on</code></pre>\r\n<h4>crontab格式说明</h4>\r\n<p>用户所建立的crontab文件中,每一行都代表一项任务,每行的每个字段代表一项设置,它的格式共分为六个字段,前五段是时间设定段,第六段是要执行的命令段,格式如下:</p>\r\n<p><img src=\"https://blog.52itstyle.vip/usr/uploads/2018/10/783800282.jpg\" /></p>\r\n<p>在以上各个字段中,还可以使用以下特殊字符:</p>\r\n<ul>\r\n<li>星号(*):代表所有可能的值,例如day字段如果是星号,则表示在满足其它字段的制约条件后每天都执行该命令操作。</li>\r\n<li>逗号(,):可以用逗号隔开的值指定一个列表范围,例如,&ldquo;1,2,5,7,8,9&rdquo;</li>\r\n<li>中杠(-):可以用整数之间的中杠表示一个整数范围,例如&ldquo;2-6&rdquo;表示&ldquo;2,3,4,5,6&rdquo;</li>\r\n<li>正斜线(/):可以用正斜线指定时间的间隔频率,例如&ldquo;0-23/2&rdquo;表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。</li>\r\n</ul>\r\n</body>\r\n</html>','tinymce'),(20,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>最近半年因为项目清闲,做了很多自学计划。很多都是心血来潮,也并没有都坚持下来,比如学习C语言、学习Spring和web基础知识、学习操作系统等。</p>\r\n<p>在这个过程中,突然意识到&ldquo;心态&rdquo;是学习中一个很重要的部分。程序员大部分是独自学习的,很容易进入一个懒惰或焦虑的极端状态,当心态不稳定时,会发现时间眼睁睁地流过,而你却无法进步,这是一种很难受的状态。当心态健康时,不仅能保持稳定的学习速度,还能在学习中得到快乐,进入一个正向循环。</p>\r\n<h2>计算机是一门实践科学</h2>\r\n<p>不要做学院派</p>\r\n<p>一开始学习时,楼主看了许多帖子,里面的Java大神们都在说:要提高技术,就要不停地看源码,甚至自己造轮子&hellip;&hellip;</p>\r\n<p>年轻的楼主深受这种理论影响,不屑于搞业务而日日研究如HashMap源码这样枯燥的东西,甚至计划自己写一套collection framework,把大神们造过的轮子再造一遍,似乎只要写完这一套东西就能立地成佛,能搞定所有的业务和bug&hellip;&hellip;</p>\r\n<p>当我开始写HashMap的时候就发现,为了写而写是一种很蠢的行为。一个轮子内部的那些看似精巧繁杂的结构,存在的意义是为了让轮子更好更长久地运转,而不是工程师为了炫技而创造的。如果我们没有一个实用的目标,反而会浪费时间,迷失在一些看上去很神秘的细节中。其实,像HashMap这样的数据结构,只需了解它大致的工作原理就可以了,余下的代码细节可以在使用有疑惑或者出了问题时再去看。</p>\r\n<p>再比如,楼主看面经中总是提到JVM调优,因此觉得这是Java程序员进阶所必备的知识,于是就买了书和教程去看,但是发现对于调优这个话题看书效率奇低,书上比较多的是一些描述性的内容,如JVM的内存分布和垃圾处理算法;还有一些实用性的内容,比如调优工具、调优案例。有些内容反复看了又看,回想起来脑子里却不剩什么,远没有看完一个算法后的那种条理清晰的感觉。而花在这个问题上的时间却远远超过看算法的时间。</p>\r\n<p>这几个周,同事要做一个自动化测试工具,问大家有什么好的建议。楼主想到自己一直学调优而无所得,灵机一动,提议在工具中加入调优功能,得到了同事认可。同事给楼主大概介绍了一些调优思路,推荐了一些资料去看。楼主希望争取到这个亲自实现调优功能的机会,于是幻想自己即将面临各种性能问题,为了解决这些问题开始储备知识,然后惊异地发现学习有了层次,楼主不再死板地按章节看书了,可以自动地筛选出重要和不重要的内容,甚至可以在脑中形成清晰的脉络了!</p>\r\n<p>所以计算机是一门实践科学这句话一定要深刻理解。因为偏实践,所以不可能当王语嫣,因为许多理论是从实践中来的,好比数学公理,只能从实践中得到感性认识,无法从其他的理论推测得来,也无法死记硬背。楼主是一个学院派的人,在这方面吃了不少苦。从中总结出的方法论是:</p>\r\n<p>计算机科学的知识大多是偏实践的,学习这些知识不能只看理论,要依托于实践。</p>\r\n<p>实践和理论应该是两条互相缠绕的螺旋线,当实践无法继续进行时就去看理论,当理论看起来无法理解更深时就去实践。</p>\r\n<p>实践最好是一个项目,但大多数情况要自己提需求,寻找实践方法。可以是个人项目,也可以是自己的各种小demo等等,形式不限,学到就行。</p>\r\n<p>技术只是人们为了解决问题而创造的,包括那些听起来很高大上的框架和名词。只要怀着负责任的信念去做事一定会接触到各种各样的问题,面临问题自然会去思考解决方法,在这个思考的过程中,你实际上就开始接触了设计模式、JVM原理、计算机组成、网络协议等等&hellip;&hellip;所以,不可以鄙视平时在公司做的那些看似简单的业务。</p>\r\n<h2>持续输出</h2>\r\n<p>主动进入正反馈</p>\r\n<p>长时间的学习很容易让人懈怠,陷入一种被动接受知识的状态。另外,一个人的思想永远不可能顾全所有方面。</p>\r\n<p>比如,楼主的朋友最近想学习Java,于是楼主自告奋勇,每天给他介绍二十分钟Java基础。当说到垃圾处理时,楼主自然地老生常谈:Java内存分为新生代和老年代,一般的GC只对新生代进行收集,而只有老年代也没有空间了才会进行Full GC&hellip;&hellip;朋友眨着天真的眼睛问楼主:为什么要分两个区进行收集,而不是每次都对所有内存进行垃圾处理呢?楼主惊讶地发现自己答不出来,因为从未想过这个问题&hellip;&hellip;</p>\r\n<p>今天楼主复习时无意中发现,书里其实有介绍这么做的原因,即大部分对象都是朝生夕死的,按年龄分代并频繁地收集新生区比较贴合这个模型,这样既能照顾到常用的内存,也能保证垃圾处理能在比较短的时间内结束。这一段楼主其实看过多遍了,但竟然从未将两者联系起来,也未思考过分区设计的原因&hellip;&hellip;</p>\r\n<p>又比如,有一次面试时面试官问Java线程池中核心线程数和最大线程数的区别。楼主轻车熟路地说:核心线程数是线程池的初始线程数,最大线程数是线程池能扩展到的最大线程数,只有任务队列提交满时才会扩容。面试官接着问道:&ldquo;那你想过为什么要这么设计吗?&rdquo;楼主支吾半天也没答上来&hellip;&hellip;事后一想,其实很简单,就是为了既能重用线程,减少线程开销,又能满足偶尔的大规模请求而已。这么简单,为何当时没想到呢?</p>\r\n<p>&hellip;&hellip;因为平时看书的时候就缺乏思考啊&hellip;&hellip;</p>\r\n<p>对于楼主这种口号上的巨人、思考上的矮子,除了经常给别人讲东西从而被别人挑战之外,还有一个好办法,就是坚持写博客。</p>\r\n<p>博客是一篇文章,需要有文章结构这种东西,为了使你的博客显得比较有逻辑性,你会被逼着去梳理知识点之间的逻辑,以及一门技术的由来、历史、产生背景等等。在梳理过程中,你会发现看书时遗漏的点被填满了。另外,把思路写出来能进一步强化你对知识的印象。而且,持续写博客会给你的学习造成一种健康的仪式感,即使一开始没有太多读者,你也会爱上这种输出的感觉,反过来督促自己为了写出更多博客而坚持学习。</p>\r\n<h2>行动紧张,心情轻松</h2>\r\n<p>不要让焦虑浪费你的时间</p>\r\n<p>焦虑是自学最大的敌人。有一阵子看到朋友看书很快,自己也想加快脚步,可是却打乱了自己的一贯节奏。又有一阵子,像着了魔一样四处搜寻名人自传、大神博客,然后发现自己跟大神的差距要以数十年计,陷入一种无止境的焦虑。</p>\r\n<p>楼主愿意相信,焦虑并不是完全不好的状态,因为它证明一个人想要进步,想跟那些优秀的人比肩。但是,&ldquo;认识自己&rdquo;也是一门非常重要的学问。每个人都是不同的,现在跟大神差距很大并不意味着几年后还会差距很大;但是有一个坚持下去的秘笈,跟跑马拉松一样,就是不要看别人,不要看终点。要做到这一点,先要接受自己的平凡,这样才能平心静气地去吸收知识、抵御外界的影响,而持续的焦虑只能适得其反。</p>\r\n<p>如果每天都抱着一个远大的梦想,正襟危坐地努力学习,会发现很快就累了。之所以看电视剧不容易累,是因为潜意识就告诉自己在玩。如果学习的时候,也可以调整到一个好玩的心态,把问题当作小乐趣去解,会发现不知不觉就沉浸在解题的乐趣中,而感受不到时间的流逝,这种状态是学习的上乘状态。</p>\r\n<h2>一些啰嗦</h2>\r\n<p>学习动力从哪里来</p>\r\n<p>楼主是个心高气傲的人,花了许多时间才接受自己是凡人这个事实。或许这个打击来得越早,就越容易踏实地走脚下的路。</p>\r\n<p>跟初高中就开始学算法、写游戏,大学拿到ACM金牌,毕业后几家大公司随便选的大神相比,楼主实在是天资愚钝,大学时仅有的一门编程课&mdash;&mdash;C语言学得异常痛苦,研究生时自学编程磕磕绊绊,做毕设时经常晚上十一点被导师的邮件揪起来&mdash;&mdash;代码又出现了问题。楼主天资平庸,工作平凡,在写代码上没有任何具体的目标,比如\"要做出一款炫酷的RPG游戏\"这样的&hellip;&hellip;</p>\r\n<p>为了这个事实,曾经非常焦虑。喜欢拿自己跟别人比,唯一的结论就是还差得太多,连别人初中的水平都不如,得赶快补上&hellip;&hellip;于是买书做计划,每天都要完成计划的指定部分,比如读十页书等等,这样也坚持了一段时间,但是因为压力比较大,学习效率实在不高,很久之后感觉还在入门边缘徘徊。</p>\r\n<p>也不是说时间不重要,毕竟有著名的\"一万小时定律\"摆在那。但是看似努力学习超过一万小时而还没成为大牛的人也是有的。从我自身的经验,还是\"进入状态\"最靠谱。拿我自己来说,进入状态后两天学到的可能比没进入状态时几个周学到的都多,绝不夸张。</p>\r\n<h2>小结</h2>\r\n<p>所以,最近越来越相信方法论了。</p>\r\n<p>如果你看到这里,说:楼主说得都很有道理,但我缺乏学习的动力,怎么办?推荐你去看一下阮一峰的《未来世界的幸存者》 。你会发现作者对未来人类文明坍塌的预言可能超出你最悲观的想象。作者认为,随着现代科技的爆发式发展,社会贫富分化将迅速加剧,中产阶级将逐渐消失。那些无法理解现代科技的人会沉到社会下层,沦为科技的奴隶;而由于AI技术的快速发展,大部分本属于中产阶级的程序员都将失业,失业后由于看不到希望,沉溺于虚拟游戏这样的&ldquo;精神毒品&rdquo;度过贫穷的一生。在这样的魔幻世界中,&ldquo;终生学习&rdquo;已经不是一句口号,而是一个人清醒地生存在世界上的必需&hellip;&hellip;</p>\r\n</body>\r\n</html>','tinymce'),(21,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>有时候我们可能会遇到git提交错误的情况,比如提交了敏感的信息或者提交了错误的版本,这个时候我们想将提交到代码库的记录删除。</p>\r\n<h4>获取要回滚到的提交点的hash值</h4>\r\n<p>首先,我们需要找到我们需要回滚到的提交点的hash,可以使用</p>\r\n<pre class=\"language-html\"><code>$ git log</code></pre>\r\n<p>命令获取提交的历史找到需要回滚到的提交点。</p>\r\n<h4>回滚</h4>\r\n<p>复制hash值,使用</p>\r\n<pre class=\"language-html\"><code>$ git reset &ndash;hard commit_hash</code></pre>\r\n<p>将head指向当前想会滚到的地方。</p>\r\n<h4>push新的head到git</h4>\r\n<p>再使用</p>\r\n<pre class=\"language-html\"><code>$ git push origin HEAD &ndash;force</code></pre>\r\n<p>将当前指向的head推到git。</p>\r\n<h2>总结</h2>\r\n<p>自己搭建的GIT服务,删除是没有问题的,但是如果你把代码提交到码云,那么你就要小心的,码云设置了两套系统,一个记录(可以删除),一个是动态(不可以删除,除非企业版本)。</p>\r\n</body>\r\n</html>','tinymce'),(22,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>大家都知道现在很多站点下载资料都是要收费的,无论是积分还是金币,想免费只能说很少很少了,那么这些网站是如何做到资源防盗链的呢?</p>\r\n<p>这里推荐一款比较容易上手的神器,Nginx本身提供了secure_link来完成防盗链功能,可以给服务器文件链接添加时间戳和校验码,从而保护服务器文件不被任意下载盗用。</p>\r\n<h2>时序图</h2>\r\n<p><img src=\"https://blog.52itstyle.vip/usr/uploads/2018/10/3204986360.png\" /></p>\r\n<h2>Nginx配置</h2>\r\n<p>如何安装Nginx这里不再赘述,安装的时候记得开启ngx_http_secure_link_module即可。</p>\r\n<pre class=\"language-html\"><code>./configure --with-http_secure_link_module #编译nginx时加入</code></pre>\r\n<p>安装完成检测:</p>\r\n<pre class=\"language-html\"><code>nginx -V</code></pre>\r\n<p>如果出现以下说明配置成功:</p>\r\n<pre class=\"language-html\"><code>onfigure arguments: --with-http_secure_link_module --prefix=/usr/local/nginx --with-http_stub_status_module</code></pre>\r\n<h2>实例配置</h2>\r\n<pre class=\"language-html\"><code>erver {\r\n listen 80;\r\n server_name download.52itstyle.com;\r\n charset utf-8;\r\n location / {\r\n #这里配置了2个参数一个是md5,一个是expires\r\n secure_link $arg_md5,$arg_expires;\r\n #md5的哈希格式为 secret+url+expires,expires为时间戳单位s,url为请求地址\r\n secure_link_md5 52itstyle$uri$arg_e;\r\n #这里我们的md5是我们按照secure_link_md5的方式计算的哈希,secure_link会比对它计算的哈希值是否与我们的md5参数一致\r\n if ($secure_link = \"\") {\r\n #资源不存在或哈希比对失败\r\n return 402;\r\n }\r\n if ($secure_link = \"0\") {\r\n #失效超时\r\n return 405;\r\n }\r\n #重命名文件名\r\n add_header Content-Disposition \"attachment;filename=$arg_f\";\r\n alias /data/site/down.52itstyle.com/;\r\n }\r\n error_page 500 502 503 504 /50x.html;\r\n error_page 402 405 /40x.html;\r\n location = /50x.html {\r\n root html;\r\n }\r\n location = /40x.html {\r\n root html;\r\n }\r\n}</code></pre>\r\n<h3>参数详解</h3>\r\n<h4>secure_link</h4>\r\n<p>语法 : secure_link expression;<br />默认值: 无<br />配置段:http, server, location</p>\r\n<p>expression由校验值和过期时间组成,其中校验值将会与 secure_link_md5中的指定参数的MD5哈希值进行对比。</p>\r\n<p>如果两个值不一致,$secure_link变量的值是空;如果两个值一致,则进行过期检查;如果过期了,则$secure_link变量值是0;如果没过期,则为1。</p>\r\n<p>如果链接是有时效性的,那么过期时间用时间戳进行设置,在MD5哈希值后面声明,用逗号隔开。如果没有设置过期时间,该链接永久有效。</p>\r\n<h4>secure_link_md5</h4>\r\n<p>语法 : secure_link_md5 expression;<br />默认值: 无<br />配置段:http, server, location</p>\r\n<p>expression指定计算md5哈希值的参数,该md5值将会和url中传递的md5值进行对比校验。expression一般包含uri(如demo.com/s/link uri则为/s/link)以及加密 密钥secret,如果该链接具有时效,则expression需包含$secure_link_expires,expression还可以加入客户端信息,如访问IP,浏览器版本信息等。</p>\r\n<h2>Java后端配置</h2>\r\n<p>案例,仅供参考:</p>\r\n<pre class=\"language-html\"><code>import org.apache.commons.codec.binary.Base64;\r\nimport org.apache.commons.codec.digest.DigestUtils;\r\n/**\r\n * 生成加密連接\r\n */\r\npublic class SecureLink {\r\n private static String site = \"https://down.52itstyle.com/\";\r\n private static String secret = \"52itstyle\";\r\n public static String createLink(String path,String fileName){\r\n String time = String.valueOf((System.currentTimeMillis() / 1000) + 300); // 5分钟有效\r\n String md5 = Base64.encodeBase64URLSafeString(DigestUtils.md5(secret + path + time));\r\n String url = site + path + \"?md5=\" + md5 + \"&amp;expires=\" + time + \"&amp;f=\"+fileName;\r\n return url;\r\n }\r\n public static void main(String[] args) {\r\n //https://down.52itstyle.com/2018101025689452.pdf?md5=FnDYyFzCooI9q8sh1Ffkxg&amp;expires=1539847995&amp;f=分布式秒杀架构.pdf\r\n System.out.println(createLink(\"2018101025689452.pdf\",\"分布式秒杀架构.pdf\"));\r\n }\r\n}</code></pre>\r\n<h2>总结</h2>\r\n<p>整个加密过程有点对称加密的意思,后端根据密钥生成加密地址,Nginx代理服务器进行解密校验,如果通过则允许下载。</p>\r\n<p>测试中还发现一个问题,生成的链接有时会报超时失效,可能是由于后端服务器和下载服务器时间不统一导致的,同步更下系统时间即可,</p>\r\n<p>如果有做积分下载服务的小伙伴这的确是一个不错的选择,需要注意的是一定要不定期的更换密钥,防止泄露。</p>\r\n<h2>参考</h2>\r\n<p><a href=\"http://nginx.org/en/docs/http/ngx_http_secure_link_module.html\">http://nginx.org/en/docs/http/ngx_http_secure_link_module.html</a></p>\r\n</body>\r\n</html>','tinymce'),(23,'<!DOCTYPE html>\r\n<html>\r\n<head>\r\n</head>\r\n<body>\r\n<h2>前言</h2>\r\n<p>在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了。在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使用的 xml 配置方式。</p>\r\n<h2>改造前</h2>\r\n<p>之前在 SpringBoot 中使用 Dubbox是这样的。先简单记录下版本,Dubbox-2.8.4、zkclient-0.6、zookeeper-3.4.6。</p>\r\n<p>项目中引入 spring-context-dubbo.xml 配置文件如下:</p>\r\n<pre class=\"language-html\"><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://code.alibabatech.com/schema/dubbo\"\r\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans\r\n http://www.springframework.org/schema/beans/spring-beans.xsd\r\n http://code.alibabatech.com/schema/dubbo\r\n http://code.alibabatech.com/schema/dubbo/dubbo.xsd\r\n \"&gt;\r\n &lt;!-- 记录监控信息 --&gt;\r\n &lt;dubbo:monitor protocol=\"registry\"/&gt;\r\n &lt;!-- 提供方应用信息,用于计算依赖关系 --&gt;\r\n &lt;dubbo:application name=\"spring-boot-pay\" /&gt;\r\n &lt;!-- 使用zookeeper注册中心暴露服务地址 subscribe 默认:true 是否向此注册中心订阅服务,如果设为false,将只注册,不订阅 check 默认:true 注册中心不存在时,是否报错 --&gt;\r\n &lt;dubbo:registry protocol=\"zookeeper\" address=\"192.168.1.180:2181\" check=\"false\"/&gt;\r\n &lt;!-- \r\n 生产者配置 生产者 远程默认调用3次 参数 retries=\"2\" async=\"true\" 异步返回结果 默认是同步 timeout=\"10000\" 毫秒\r\n 用dubbo协议在20882端口暴露服务 固定线程池 10 启动时建立线程,不关闭,一直持有 负载均衡策略 轮询\r\n --&gt;\r\n &lt;dubbo:provider timeout=\"10000\" threads=\"10\" threadpool=\"fixed\" loadbalance=\"roundrobin\"/&gt;\r\n &lt;!-- name=\"dubbo\" 协议名称 为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护。 host 部署外网设置为内网通信地址--&gt;\r\n &lt;dubbo:protocol name=\"dubbo\" port=\"-1\" dispatcher=\"all\" accepts=\"1000\" /&gt;\r\n \r\n &lt;!-- 使用注解方式--&gt; \r\n &lt;dubbo:annotation package=\"com.itstyle\"/&gt;\r\n&lt;/beans&gt;</code></pre>\r\n<p>&nbsp;</p>\r\n<p>启动类引入以下注解:</p>\r\n<pre class=\"language-html\"><code>@SpringBootApplication\r\n@ImportResource({\"classpath:spring-context-dubbo.xml\"})\r\npublic class Application{\r\n private static final Logger logger = Logger.getLogger(Application.class);\r\n\r\n public static void main(String[] args) throws InterruptedException,\r\n IOException {\r\n logger.info(\"支付项目启动 \");\r\n }\r\n\r\n}</code></pre>\r\n<h2>改造后</h2>\r\n<p>然而 SpringBoot 引入了新的概念 Spring Boot Starter,它有效的降低了项目开发过程的复杂程度,对于简化开发操作有着非常好的效果。</p>\r\n<h3>starter的理念</h3>\r\n<p>starter 会把所有用到的依赖都给包含进来,避免了开发者自己去引入依赖所带来的麻烦。</p>\r\n<p>需要注意的是不同的 starter 是为了解决不同的依赖,所以它们内部的实现可能会有很大的差异,例如 jpa 的starter 和 Redis 的 starter 可能实现就不一样,这是因为 starter 的本质在于 synthesize,这是一层在逻辑层面的抽象,也许这种理念有点类似于 Docker,因为它们都是在做一个&ldquo;包装&rdquo;的操作,如果你知道 Docker 是为了解决什么问题的,也许你可以用 Docker 和 starter 做一个类比。</p>\r\n<h4>starter的实现</h4>\r\n<p>虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfiguration。</p>\r\n<p>因为Spring Boot坚信&ldquo;约定大于配置&rdquo;这一理念,所以我们使用ConfigurationProperties来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。</p>\r\n<p>除此之外,starter的ConfigurationProperties还使得所有的配置属性被聚集到一个文件中(一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。</p>\r\n<h4>starter的整体逻辑</h4>\r\n<p><img src=\"https://blog.52itstyle.vip/usr/uploads/2018/10/2655930392.png\" /></p>\r\n<p>强如Dubbo,当然也会创建属于自己的 starter 来迎合Spring Boot 的火热。</p>\r\n<p>这里我们使用Dubbo比较新的版本,pom.xml 引入以下:</p>\r\n<pre class=\"language-html\"><code>&lt;!-- dubbo 替换 dubbox--&gt;\r\n&lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba&lt;/groupId&gt;\r\n &lt;artifactId&gt;dubbo&lt;/artifactId&gt;\r\n &lt;version&gt;2.6.2&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n&lt;dependency&gt;\r\n &lt;groupId&gt;com.alibaba.spring.boot&lt;/groupId&gt;\r\n &lt;artifactId&gt;dubbo-spring-boot-starter&lt;/artifactId&gt;\r\n &lt;version&gt;2.0.0&lt;/version&gt;\r\n&lt;/dependency&gt;\r\n&lt;!-- curator-recipes 替换 zkclient--&gt;\r\n&lt;dependency&gt;\r\n &lt;groupId&gt;org.apache.curator&lt;/groupId&gt;\r\n &lt;artifactId&gt;curator-recipes&lt;/artifactId&gt;\r\n &lt;version&gt;4.0.1&lt;/version&gt;\r\n&lt;/dependency&gt;</code></pre>\r\n<p>application.properties 配置:</p>\r\n<pre class=\"language-html\"><code>## dubbo springboot 配置\r\nspring.dubbo.application.id=springboot_pay\r\nspring.dubbo.application.name=springboot_pay\r\nspring.dubbo.registry.address=zookeeper://192.168.1.127:2181\r\nspring.dubbo.provider.threads=10\r\nspring.dubbo.provider.threadpool=fixed\r\nspring.dubbo.provider.loadbalance=roundrobin\r\nspring.dubbo.server=true\r\nspring.dubbo.protocol.name=dubbo</code></pre>\r\n<p>启动类加入以下注解:</p>\r\n<pre class=\"language-html\"><code>@EnableDubboConfiguration\r\n@SpringBootApplication\r\npublic class Application{\r\n private static final Logger logger = Logger.getLogger(Application.class);\r\n\r\n public static void main(String[] args) throws InterruptedException,\r\n IOException {\r\n logger.info(\"支付项目启动 \");\r\n }\r\n\r\n}</code></pre>\r\n<p>相关暴露接口实现配置:</p>\r\n<pre class=\"language-html\"><code>import org.springframework.stereotype.Component;\r\nimport com.alibaba.dubbo.config.annotation.Service;\r\n\r\n@Service\r\n@Component\r\npublic class AliPayServiceImpl implements IAliPayService {\r\n //省略代码\r\n}\r\n</code></pre>\r\n<p>最后启动服务,如果启动成功并注册到注册中心,说明改造成功。</p>\r\n<h2>补充</h2>\r\n<p>Dubbo 2.6.1 是改变结构后首次发布的版本,Dubbo 2.6.0 已合并当当网提供的 Dubbox 分支。</p>\r\n<p>Dubbo的版本策略:两个大版本并行发展,2.5.x是稳定版本,2.6.x是新功能实验版本。2.6上实验都稳定了以后,会迁移到2.5。</p>\r\n<h2>总结</h2>\r\n<ul>\r\n<li>原当当 Dubbox 2.8.4 替换为 Dubbo 2.6.2</li>\r\n<li>原 spring-context-dubbo.xml 配置 替换为 dubbo-spring-boot-starter 2.0.0</li>\r\n<li>原 zkclient 0.6 替换为 curator-recipes 4.0.1</li>\r\n<li>原 zookeeper 3.4.6 升级为 zookeeper 3.5.3</li>\r\n</ul>\r\n<h2>案例</h2>\r\n<p>支付宝,微信,银联详细代码案例:<a href=\"https://gitee.com/52itstyle/spring-boot-pay\">https://gitee.com/52itstyle/spring-boot-pay</a></p>\r\n<h2>参考</h2>\r\n<p><a href=\"https://github.com/apache/incubator-dubbo\">https://github.com/apache/incubator-dubbo</a></p>\r\n<p><a href=\"https://github.com/alibaba/dubbo-spring-boot-starter/blob/master/README_zh.md\">https://github.com/alibaba/dubbo-spring-boot-starter/blob/master/README_zh.md</a></p>\r\n<p><a href=\"https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters\">https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters</a></p>\r\n<p><a href=\"https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/\">https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/</a></p>\r\n</body>\r\n</html>','tinymce');
/*!40000 ALTER TABLE `mto_post_attribute` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_post_tag`
--
DROP TABLE IF EXISTS `mto_post_tag`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_post_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`post_id` bigint(20) DEFAULT NULL,
`tag_id` bigint(20) DEFAULT NULL,
`weight` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `IK_TAG_ID` (`tag_id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_post_tag`
--
LOCK TABLES `mto_post_tag` WRITE;
/*!40000 ALTER TABLE `mto_post_tag` DISABLE KEYS */;
INSERT INTO `mto_post_tag` VALUES (1,4,1,1558516723499),(2,5,2,1558517045454),(3,6,3,1558517141656),(4,12,4,1558576193960),(5,12,5,1558576193988),(6,13,6,1558576350337),(7,14,7,1558576454543),(8,15,8,1558576711294),(9,17,9,1558577278591),(10,19,10,1558577563859),(11,21,11,1558577698903);
/*!40000 ALTER TABLE `mto_post_tag` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_security_code`
--
DROP TABLE IF EXISTS `mto_security_code`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_security_code` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` varchar(16) NOT NULL,
`created` datetime NOT NULL,
`expired` datetime NOT NULL,
`key_` varchar(64) NOT NULL,
`status` int(11) DEFAULT NULL,
`target` varchar(64) DEFAULT NULL,
`type` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_shxjkbkgnpxa80pnv4ts57o19` (`key_`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_security_code`
--
LOCK TABLES `mto_security_code` WRITE;
/*!40000 ALTER TABLE `mto_security_code` DISABLE KEYS */;
/*!40000 ALTER TABLE `mto_security_code` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_tag`
--
DROP TABLE IF EXISTS `mto_tag`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created` datetime DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`latest_post_id` bigint(20) NOT NULL,
`name` varchar(32) NOT NULL,
`posts` int(11) NOT NULL,
`thumbnail` varchar(128) DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_9ki38gg59bw5agwxsc4xtednf` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_tag`
--
LOCK TABLES `mto_tag` WRITE;
/*!40000 ALTER TABLE `mto_tag` DISABLE KEYS */;
INSERT INTO `mto_tag` VALUES (1,'2019-05-22 17:18:43',NULL,4,'redis',1,NULL,'2019-05-22 17:18:43'),(2,'2019-05-22 17:24:05',NULL,5,'Redis,分布式',1,NULL,'2019-05-22 17:24:05'),(3,'2019-05-22 17:25:42',NULL,6,'springboot,事务',1,NULL,'2019-05-22 17:25:42'),(4,'2019-05-23 09:49:54',NULL,12,'session',1,NULL,'2019-05-23 09:49:54'),(5,'2019-05-23 09:49:54',NULL,12,'springboot',1,NULL,'2019-05-23 09:49:54'),(6,'2019-05-23 09:52:11',NULL,13,'跨域',1,NULL,'2019-05-23 09:52:11'),(7,'2019-05-23 09:54:15',NULL,14,'token',1,NULL,'2019-05-23 09:54:15'),(8,'2019-05-23 09:58:31',NULL,15,'ActiveMq springboot',1,NULL,'2019-05-23 09:58:31'),(9,'2019-05-23 10:07:59',NULL,17,'Email',1,NULL,'2019-05-23 10:07:59'),(10,'2019-05-23 10:12:44',NULL,19,'Nginx',1,NULL,'2019-05-23 10:12:44'),(11,'2019-05-23 10:14:59',NULL,21,'git',1,NULL,'2019-05-23 10:14:59');
/*!40000 ALTER TABLE `mto_tag` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_user`
--
DROP TABLE IF EXISTS `mto_user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`name` varchar(32) DEFAULT NULL,
`avatar` varchar(128) DEFAULT '/dist/images/ava/default.png',
`email` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
`status` int(5) NOT NULL,
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
`last_login` datetime DEFAULT NULL,
`gender` int(5) NOT NULL,
`role_id` int(11) DEFAULT NULL,
`comments` int(11) NOT NULL,
`posts` int(11) NOT NULL,
`signature` varchar(140) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_USERNAME` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_user`
--
LOCK TABLES `mto_user` WRITE;
/*!40000 ALTER TABLE `mto_user` DISABLE KEYS */;
INSERT INTO `mto_user` VALUES (1,'admin','炜丿码农','/storage/avatars/000/000/001_240.jpg','example@mtons.com','3TGCQF25BLHU9R7IQUITN0FCC5',0,'2017-08-06 17:52:41','2017-07-26 11:08:36','2019-05-23 10:22:58',0,1,3,22,'人生随时需要调整'),(2,'admins','遗失的角落','/dist/images/ava/default.png',NULL,'UUKHSDDI5KPA43A8VL06V0TU2',0,'2019-05-14 17:34:46',NULL,'2019-05-14 17:35:44',0,NULL,1,0,'');
/*!40000 ALTER TABLE `mto_user` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mto_user_oauth`
--
DROP TABLE IF EXISTS `mto_user_oauth`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mto_user_oauth` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL,
`access_token` varchar(128) DEFAULT NULL,
`expire_in` varchar(128) DEFAULT NULL,
`oauth_code` varchar(128) DEFAULT NULL,
`oauth_type` int(11) DEFAULT NULL,
`oauth_user_id` varchar(128) DEFAULT NULL,
`refresh_token` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `mto_user_oauth`
--
LOCK TABLES `mto_user_oauth` WRITE;
/*!40000 ALTER TABLE `mto_user_oauth` DISABLE KEYS */;
/*!40000 ALTER TABLE `mto_user_oauth` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `shiro_permission`
--
DROP TABLE IF EXISTS `shiro_permission`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shiro_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`description` varchar(140) DEFAULT NULL,
`name` varchar(32) NOT NULL,
`parent_id` bigint(11) DEFAULT NULL,
`version` int(11) DEFAULT NULL,
`weight` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_NAME` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `shiro_permission`
--
LOCK TABLES `shiro_permission` WRITE;
/*!40000 ALTER TABLE `shiro_permission` DISABLE KEYS */;
INSERT INTO `shiro_permission` VALUES (1,'进入后台','admin',0,0,0),(2,'栏目管理','channel:list',0,0,0),(3,'编辑栏目','channel:update',2,0,0),(4,'删除栏目','channel:delete',2,0,0),(5,'文章管理','post:list',0,0,0),(6,'编辑文章','post:update',5,0,0),(7,'删除文章','post:delete',5,0,0),(8,'评论管理','comment:list',0,0,0),(9,'删除评论','comment:delete',8,0,0),(10,'用户管理','user:list',0,0,0),(11,'用户授权','user:role',10,0,0),(12,'修改密码','user:pwd',10,0,0),(13,'激活用户','user:open',10,0,0),(14,'关闭用户','user:close',10,0,0),(15,'角色管理','role:list',0,0,0),(16,'修改角色','role:update',15,0,0),(17,'删除角色','role:delete',15,0,0),(18,'主题管理','theme:index',0,0,0),(19,'系统配置','options:index',0,0,0),(20,'修改配置','options:update',19,0,0);
/*!40000 ALTER TABLE `shiro_permission` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `shiro_role`
--
DROP TABLE IF EXISTS `shiro_role`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shiro_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`description` varchar(140) DEFAULT NULL,
`name` varchar(32) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `shiro_role`
--
LOCK TABLES `shiro_role` WRITE;
/*!40000 ALTER TABLE `shiro_role` DISABLE KEYS */;
INSERT INTO `shiro_role` VALUES (1,NULL,'admin',0);
/*!40000 ALTER TABLE `shiro_role` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `shiro_role_permission`
--
DROP TABLE IF EXISTS `shiro_role_permission`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shiro_role_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`permission_id` bigint(20) DEFAULT NULL,
`role_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `shiro_role_permission`
--
LOCK TABLES `shiro_role_permission` WRITE;
/*!40000 ALTER TABLE `shiro_role_permission` DISABLE KEYS */;
INSERT INTO `shiro_role_permission` VALUES (1,1,1),(2,2,1),(3,3,1),(4,4,1),(5,5,1),(6,6,1),(7,7,1),(8,8,1),(9,9,1),(10,10,1),(11,11,1),(12,12,1),(13,13,1),(14,14,1),(15,15,1),(16,16,1),(17,17,1),(18,18,1),(19,19,1),(20,20,1);
/*!40000 ALTER TABLE `shiro_role_permission` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `shiro_user_role`
--
DROP TABLE IF EXISTS `shiro_user_role`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `shiro_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `shiro_user_role`
--
LOCK TABLES `shiro_user_role` WRITE;
/*!40000 ALTER TABLE `shiro_user_role` DISABLE KEYS */;
INSERT INTO `shiro_user_role` VALUES (1,1,1);
/*!40000 ALTER TABLE `shiro_user_role` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2019-05-23 10:35:14

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/qwls-springboot_shiro_redis.git
git@api.gitlife.ru:oschina-mirror/qwls-springboot_shiro_redis.git
oschina-mirror
qwls-springboot_shiro_redis
qwls-springboot_shiro_redis
master