个人随笔
原 Centos7安装Redis

一、安装Redis1.下载Rediswgethttp://download.redis.io/releases/redis-4.0.6.tar.gz2.解压tar-zxvfredis-4.0.6.tar.gz3.yum安装gccyuminstall-ygcc4.编译安装rediscdredis-4.0.6makeMALLOC=libccdsrc&&makeinstall二、启动Redis1.在/etc目录下新建redis目录mkdir-p/etc/redis2.将/usr/local/redis-4.0.6/redis.conf文件复制一份到/etc/redis目录下,并命名为6379.confcp/usr/local/redis-4.0.6/redis.conf/etc/redis/6379.conf3.将redis的启动脚本复制一份放到/etc/init.d目录下cp/usr/local/redis-4.0.6/utils/redis_init_script/etc/init.d/redisd4.设置redis开机自启动cd/etc/init.dchkconfigredisdonserviceredisddoesnotsupportchkconfig看结果是redisd不支持chkconfig,编辑redisd文件,加入如下注释:#chkconfig:23459010#description:Redisisapersistentkey-valuedatabasechkconfigredisdon启动redis:serviceredisdstart停止redis:serviceredisdstop

开发项目专栏
原 SpringBoot+Thymleaf项目初入(四) - 用户登录页面优化

1.新建CSS和JS文件2.新建静态资源配置类:ImsConfig.javapackagecn.coralcloud.ims.config;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;/***@authorc-geff*@nameImsConfig*@description*@date2020-11-0315:28*/@ConfigurationpublicclassImsConfigextendsWebMvcConfigurerAdapter{@OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");super.addResourceHandlers(registry);}}注:新建config包,然后新建ImsConfig类,继承自WebMvcConfigurerAdapter,实现了addResourceHandlers方法,该方法设置了访问/static/路径的文件时会映射到项目static文件夹下3.login.ftl修改,head添加CSS引入:4.login.ftl修改,页面布局修改:5.编写对应的main.css文件,该文件为通用样式html,body{padding:0;margin:0;}.ims-form-label{font-size:15px;color:rgba(0,0,0,.7);width:100px;height:40px;line-height:40px;letter-spacing:3px;}.ims-form-input{height:40px;line-height:40px;flex:1;outline:0;padding:015px;border:1pxsolid#DCDFE6;border-radius:4px;}.ims-form-input:focus{border-color:#409EFF;}.ims-form-item{margin:15px0;width:100%;display:flex;}.ims-button{height:40px;line-height:40px;border-radius:4px;padding:030px;border:none;color:#FFFFFF;font-size:14px;background-color:#409EFF;cursor:pointer;margin:15px0;outline:0;}.ims-button:hover{background-color:#3888e0;}.ims-button:focus{background-color:rgba(64,158,255,0.81);}6.编写对应的login.css文件,该文件为登录页专用样式.login-container{display:flex;align-content:center;justify-content:center;text-align:center;-webkit-box-pack:center;-webkit-box-align:center;align-items:center;width:100vw;height:100vh;background-image:url("/static/images/login_bg.jpg");background-repeat:no-repeat;background-size:100%100%;}.login-containerform{width:350px;height:300px;background-color:rgba(255,255,255,.7);border-radius:5px;padding:10px20px;}.login-container.ims-button{width:100%}.errormsg{color:#ed2322;font-size:13px;margin-bottom:0;}7.static目录下新建文件夹images保存背景图片login_bg.jpglogin_bg.jpg

工作日志
原 FastDFS服务器迁移

FastDFS服务器迁移1.1目标服务器安装FastDFS首先按照上一篇文章《FastDFS介绍和安装》搭建好新的FastDFS服务器(Tracker,Storage),先配置好不用启动1.2修改配置文件修改新的Storage配置文件/etc/fdfs/storage.conf将tracker_server的IP修改为旧的Tracker服务器IP1.3启动目标Storage进程,同步数据servicefdfs_storagedrestart查看同步进程,在旧tracker服务器执行:fdfs_monitor/etc/fdfs/client.conf可以看到同步状态:等待数据同步...ACTIVE标识表示数据已经同步完成数据同步完成后,停掉Storage服务servicefdfs_storagedstop1.4修改storage配置文件修改/etc/fdfs/storage.conf文件将tracker_server的IP修改为新的tracker服务器IP1.5修改.data_init_flag文件该文件所在位置为storage.conf文件中所配置的base_path路径后面的data路径下,如配置为/home/data/fastdfs/storage,则文件所在位置为/home/data/fastdfs/storage/data/下将文件中sync_src_server配置项留空,其它配置项不变sync_src_server=1.6启动进程启动新的storage服务器进程,启动新的tracker服务器进程,在新tracker服务器下fdfs_monitor/etc/fdfs/client.conf查看Storage状态

Java开发
原 MyBatis之where关键字与<where>标签的区别

1.在使用mybatis的动态sql时,有时候遇到根据条件判断添加where后面的筛选条件的情况,会出现多余的AND或者OR:2.使用where关键字:2.1当第一个参数为空时,拼接后的sql为:select*fromtdwhereandphone=.......;2.2当所有的参数都为空时,拼接后的sql为:select*fromtdwhere.....,显然这样的sql不是完整的sql,执行时会报错.3.使用where标签时:3.1当第一个参数为空时,拼接后的sql为:select*fromtdwherephone=......(若语句的开头为AND或者OR时,where元素会将他们去除).3.2当所有的参数都为空时,拼接后的sql为:select*fromtd.(where元素只会在至少有一个子元素的条件返回SQL子句的情况下才去插入“WHERE”子句)。

Tags: JAVA
Java开发
原 SpringBoot框架之@Controller和@RestController的区别?

1.@RestController注解相当于@ResponseBody+@Controller合在一起的作用。2.如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是Return里的内容。3.如果需要返回到指定页面,则需要用@Controller配合视图解析器InternalResourceViewResolver才行。4.如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。)

Tags: JAVA
工作日志
原 【基于ELK搭建日志分析系统】- ElasticSearch安装

存储集群应与业务服务器不在同一台机器,此处搭建ES存储集群使用单节点的方式。ES集群机器需要实现安装配置好JAVA_HOME环境变量。安装配置下载,版本应与filebeat版本一致:官网地址解压修改elasticsearch.yml配置文件:其中cluster.name避免使用默认名称,各节点中配置为相同的名称,ES就会自动搜索加入启动elasticsearch:==ES集群启动不允许使用root账户==,所以应该先创建一个用户,使用新创建的用户启动elasticsearchgroupaddelkuseradd-gelkelkpasswdelk#创建elk用户密码chown-Relk:elk$ELASTICSEARCH_HOMEsuelk$ELASTICSEARCH_HOME/bin/elasticsearch-d#后台启动ES启动完成后测试,浏览器输入http://host:9200测试,有如下结果则为正常:问题解决启动elasticsearch时出现:elasticsearch:which:nojavain(/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin)[解决]在$ES_HOME/config/elasticsearch文件中加入以下配置:JAVA_HOME=/usr/local/jdk1.8maxnumberofthreads[1024]foruser[elk]likelytoolow,increasetoatleast[2048].[解决]切换到root用户,进入limits.d目录下修改配置文件:vi/etc/security/limits.d/90-nproc.conf找到并修改为softnproc2048hardnproc4096maxfiledescriptors[4096]forelasticsearchprocesslikelytoolow,increasetoatleast[65536][解决]切换到root用户,进入limits.d目录下修改配置文件。vi/etc/security/limits.d/90-nproc.conf找到并修改为softnofile65536hardnofile131072maxvirtualmemoryareasvm.max_map_count[65530]likelytoolow,increasetoatleast[262144][解决]修改:vim/etc/sysctl.conf添加下面配置:vm.max_map_count=655360并执行命令:sysctl-p

开发项目专栏
原 SpringBoot+Thymleaf项目初入(五) - 图片验证码

1.验证码工具类1.新建包utils2.新建VerifyCodeUtil类packagecn.coralcloud.ims.utils;importjavax.imageio.ImageIO;importjava.awt.*;importjava.awt.geom.AffineTransform;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.util.Arrays;importjava.util.Random;/***@authorc-geff*/publicclassVerifyCodeUtil{/***使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符*/privatestaticfinalStringVERIFY_CODES=&quot;23456789ABCDEFGHJKLMNPQRSTUVWXYZ&quot;;privatestaticRandomrandom=newRandom();/***使用系统默认字符源生成验证码*@paramverifySize验证码长度*@return*/publicstaticStringgenerateVerifyCode(intverifySize){returngenerateVerifyCode(verifySize,VERIFY_CODES);}/***使用指定源生成验证码*@paramverifySize验证码长度*@paramsources验证码字符源*@return*/publicstaticStringgenerateVerifyCode(intverifySize,Stringsources){if(sources==null||sources.length()==0){sources=VERIFY_CODES;}intcodesLen=sources.length();Randomrand=newRandom(System.currentTimeMillis());StringBuilderverifyCode=newStringBuilder(verifySize);for(inti=0;i&lt;verifySize;i++){verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));}returnverifyCode.toString();}/***生成随机验证码文件,并返回验证码值*@paramw*@paramh*@paramoutputFile*@paramverifySize*@return*@throwsIOException*/publicstaticStringoutputVerifyImage(intw,inth,FileoutputFile,intverifySize)throwsIOException{StringverifyCode=generateVerifyCode(verifySize);outputImage(w,h,outputFile,verifyCode);returnverifyCode;}/***输出随机验证码图片流,并返回验证码值*@paramw*@paramh*@paramos*@paramverifySize*@return*@throwsIOException*/publicstaticStringoutputVerifyImage(intw,inth,OutputStreamos,intverifySize)throwsIOException{StringverifyCode=generateVerifyCode(verifySize);outputImage(w,h,os,verifyCode);returnverifyCode;}/***生成指定验证码图像文件*@paramw*@paramh*@paramoutputFile*@paramcode*@throwsIOException*/publicstaticvoidoutputImage(intw,inth,FileoutputFile,Stringcode)throwsIOException{if(outputFile==null){return;}Filedir=outputFile.getParentFile();if(!dir.exists()){dir.mkdirs();}try{outputFile.createNewFile();FileOutputStreamfos=newFileOutputStream(outputFile);outputImage(w,h,fos,code);fos.close();}catch(IOExceptione){throwe;}}/***输出指定验证码图片流*@paramw*@paramh*@paramos*@paramcode*@throwsIOException*/publicstaticvoidoutputImage(intw,inth,OutputStreamos,Stringcode)throwsIOException{intverifySize=code.length();BufferedImageimage=newBufferedImage(w,h,BufferedImage.TYPE_INT_RGB);Randomrand=newRandom();Graphics2Dg2=image.createGraphics();g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);Color[]colors=newColor[5];Color[]colorSpaces=newColor[]{Color.WHITE,Color.CYAN,Color.GRAY,Color.LIGHT_GRAY,Color.MAGENTA,Color.ORANGE,Color.PINK,Color.YELLOW};float[]fractions=newfloat[colors.length];for(inti=0;i&lt;colors.length;i++){colors[i]=colorSpaces[rand.nextInt(colorSpaces.length)];fractions[i]=rand.nextFloat();}Arrays.sort(fractions);//设置边框色g2.setColor(Color.GRAY);g2.fillRect(0,0,w,h);Colorc=getRandColor(200,250);//设置背景色g2.setColor(c);g2.fillRect(0,2,w,h-4);//绘制干扰线Randomrandom=newRandom();//设置线条的颜色g2.setColor(getRandColor(160,200));intloop=20;for(inti=0;i&lt;loop;i++){intx=random.nextInt(w-1);inty=random.nextInt(h-1);intxl=random.nextInt(6)+1;intyl=random.nextInt(12)+1;g2.drawLine(x,y,x+xl+40,y+yl+20);}//添加噪点//噪声率floatyawpRate=0.05f;intarea=(int)(yawpRate*w*h);for(inti=0;i&lt;area;i++){intx=random.nextInt(w);inty=random.nextInt(h);intrgb=getRandomIntColor();image.setRGB(x,y,rgb);}//使图片扭曲shear(g2,w,h,c);g2.setColor(getRandColor(100,160));intfontSize=h-4;Fontfont=newFont(&quot;Algerian&quot;,Font.ITALIC,fontSize);g2.setFont(font);char[]chars=code.toCharArray();for(inti=0;i&lt;verifySize;i++){AffineTransformaffine=newAffineTransform();affine.setToRotation(Math.PI/4*rand.nextDouble()*(rand.nextBoolean()?1:-1),(w/verifySize)*i+fontSize/2,h/2);g2.setTransform(affine);g2.drawChars(chars,i,1,((w-10)/verifySize)*i+5,h/2+fontSize/2-10);}g2.dispose();ImageIO.write(image,&quot;jpg&quot;,os);}privatestaticfinalIntegerCOLOR_MAX_INT=255;privatestaticColorgetRandColor(intfc,intbc){if(fc&gt;COLOR_MAX_INT){fc=COLOR_MAX_INT;}if(bc&gt;COLOR_MAX_INT){bc=COLOR_MAX_INT;}intr=fc+random.nextInt(bc-fc);intg=fc+random.nextInt(bc-fc);intb=fc+random.nextInt(bc-fc);returnnewColor(r,g,b);}privatestaticintgetRandomIntColor(){int[]rgb=getRandomRgb();intcolor=0;for(intc:rgb){color=color&lt;&lt;8;color=color|c;}returncolor;}privatestaticint[]getRandomRgb(){int[]rgb=newint[3];intloop=3;for(inti=0;i&lt;loop;i++){rgb[i]=random.nextInt(255);}returnrgb;}privatestaticvoidshear(Graphicsg,intw1,inth1,Colorcolor){shearx(g,w1,h1,color);sheary(g,w1,h1,color);}privatestaticvoidshearx(Graphicsg,intw1,inth1,Colorcolor){intperiod=random.nextInt(2);booleanborderGap=true;intframes=1;intphase=random.nextInt(2);for(inti=0;i&lt;h1;i++){doubled=(double)(period&gt;&gt;1)*Math.sin((double)i/(double)period+(6.2831853071795862D*(double)phase)/(double)frames);g.copyArea(0,i,w1,1,(int)d,0);if(borderGap){g.setColor(color);g.drawLine((int)d,i,0,i);g.drawLine((int)d+w1,i,w1,i);}}}privatestaticvoidsheary(Graphicsg,intw1,inth1,Colorcolor){intperiod=random.nextInt(40)+10;booleanborderGap=true;intframes=20;intphase=7;for(inti=0;i&lt;w1;i++){doubled=(double)(period&gt;&gt;1)*Math.sin((double)i/(double)period+(6.2831853071795862D*(double)phase)/(double)frames);g.copyArea(i,0,1,h1,0,(int)d);if(borderGap){g.setColor(color);g.drawLine(i,(int)d,i,0);g.drawLine(i,(int)d+h1,i,h1);}}}}2.UserController新增验证码接口2.1新增captcha方法@GetMapping(&quot;/captcha&quot;)publicvoidcaptcha(HttpServletResponseresponse,HttpSessionsession){Stringcode=VerifyCodeUtil.generateVerifyCode(4);session.setAttribute(SessionKey.ADMIN_CAPTCHA_KEY,code);try{VerifyCodeUtil.outputImage(150,50,response.getOutputStream(),code);}catch(IOExceptione){e.printStackTrace();}}2.2修改login方法,增加验证下方为最终的登录验证方法:@PostMapping(&quot;/login&quot;)publicModelAndViewlogin(Stringemail,Stringpassword,Stringcaptcha,HttpSessionsession){StringsessionCaptcha=(String)session.getAttribute(SessionKey.ADMIN_CAPTCHA_KEY);ModelAndViewview=newModelAndView();view.setViewName(&quot;user/login&quot;);//将email和password写回到页面,使得登录失败时输入的账号密码不会丢失view.addObject(&quot;email&quot;,email);view.addObject(&quot;password&quot;,password);if(StringUtils.isEmpty(captcha)||!Objects.equals(captcha,sessionCaptcha)){view.addObject(&quot;errmsg&quot;,&quot;验证码错误!&quot;);returnview;}Useruser=userService.login(email,password);if(user!=null){session.setAttribute(SessionKey.ADMIN_USER_KEY,user);view.setViewName(&quot;redirect:/index&quot;);returnview;}view.addObject(&quot;errmsg&quot;,&quot;用户名或密码错误!&quot;);returnview;}注:此处将Session的Key值通过一个常量类SessionKey保存2.3SessionKey类在utils包下新建SessionKey类packagecn.coralcloud.ims.utils;/***@authorc-geff*@nameSessionKey*@description*@date2020-11-0410:12*/publicclassSessionKey{publicstaticfinalStringADMIN_USER_KEY=&quot;AdminUserKey&quot;;publicstaticfinalStringADMIN_CAPTCHA_KEY=&quot;AdminLoginCaptchaCode&quot;;}3.修改login.ftl3.1引入jquery下载jqeury文件jquery.min.js在/static/js文件夹下新建jquery文件夹,将jquery.min.js复制到文件夹下修改login.ftl文件,最终login.ftl内容为下:&lt;!doctypehtml&gt;&lt;htmllang=&quot;en&quot;xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;&lt;head&gt;&lt;metacharset=&quot;UTF-8&quot;&gt;&lt;metaname=&quot;viewport&quot;content=&quot;width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0&quot;&gt;&lt;metahttp-equiv=&quot;X-UA-Compatible&quot;content=&quot;ie=edge&quot;&gt;&lt;linktype=&quot;text/css&quot;rel=&quot;stylesheet&quot;href=&quot;/static/css/main.css&quot;&gt;&lt;linktype=&quot;text/css&quot;rel=&quot;stylesheet&quot;href=&quot;/static/css/login.css&quot;&gt;&lt;scriptsrc=&quot;/static/js/jquery/jquery.min.js&quot;type=&quot;application/javascript&quot;&gt;&lt;/script&gt;&lt;title&gt;用户登录&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;divclass=&quot;login-container&quot;&gt;&lt;formaction=&quot;/user/login&quot;method=&quot;post&quot;&gt;&lt;div&gt;&lt;h3&gt;用户登录&lt;/h3&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;email&quot;&gt;登录邮箱&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;email&quot;autocomplete=&quot;off&quot;th:value=&quot;${email}&quot;placeholder=&quot;请输入邮箱&quot;type=&quot;text&quot;name=&quot;email&quot;&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;password&quot;&gt;登录密码&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;password&quot;autocomplete=&quot;new-password&quot;th:value=&quot;${password}&quot;placeholder=&quot;请输入密码&quot;type=&quot;password&quot;name=&quot;password&quot;&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;captcha&quot;&gt;图片验证&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;captcha&quot;type=&quot;text&quot;placeholder=&quot;请输入图片验证码&quot;name=&quot;captcha&quot;&gt;&lt;imgsrc=&quot;/user/captcha&quot;class=&quot;captcha&quot;alt=&quot;图片验证码&quot;&gt;&lt;/div&gt;&lt;pclass=&quot;errormsg&quot;th:if=&quot;${errmsg}!=null&quot;th:text=&quot;${errmsg}&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;buttontype=&quot;submit&quot;class=&quot;ims-button&quot;&gt;登录&lt;/button&gt;&lt;/div&gt;&lt;/form&gt;&lt;/div&gt;&lt;/body&gt;&lt;scripttype=&quot;application/javascript&quot;&gt;$(document).ready(function(){$(&#39;img.captcha&#39;).click(function(){$(this).attr(&quot;src&quot;,&quot;/user/captcha?_=&quot;+newDate().getTime())})})&lt;/script&gt;&lt;/html&gt;注,login.ftl变更如下:1.head新增引入jquery.min.js2.input新增读取前面保存的email和password3.新增图片验证码输入框及图片4.新建图片点击监听事件,更换图片验证码4.修改样式login.css#captcha{width:100px;border-top-right-radius:0;border-bottom-right-radius:0;}.captcha{width:1px;flex:1;height:42px;cursor:pointer;border-top-right-radius:4px;border-bottom-right-radius:4px;}5.最终效果

转载分享
转 Spring Boot 集成Sharding-jdbc + Mybatis-Plus实现分库分表

一、Sharding-jdbc简介Sharding-jdbc是开源的数据库操作中间件;定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。官方文档地址:https://shardingsphere.apache.org/document/current/cn/overview/本文demo实现了分库分表功能。作者能力有限,如有错误,欢迎各位在评论中指出。不胜感激!二、项目结构首先创建一个一般的Springboot项目,项目采用三层架构,结构图如下:项目目录结构图POM.xml文件如下:&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;projectxmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;&lt;parent&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;&lt;version&gt;2.1.6.RELEASE&lt;/version&gt;&lt;relativePath/&gt;&lt;!--lookupparentfromrepository--&gt;&lt;/parent&gt;&lt;groupId&gt;com.macky&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-shardingjdbc&lt;/artifactId&gt;&lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;&lt;name&gt;spring-boot-shardingjdbc&lt;/name&gt;&lt;description&gt;Demoprojectforspring-boot-shardingjdbc&lt;/description&gt;&lt;properties&gt;&lt;java.version&gt;1.8&lt;/java.version&gt;&lt;/properties&gt;&lt;dependencies&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;&lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;&lt;!--mysql--&gt;&lt;dependency&gt;&lt;groupId&gt;mysql&lt;/groupId&gt;&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;!--Mybatis-Plus--&gt;&lt;dependency&gt;&lt;groupId&gt;com.baomidou&lt;/groupId&gt;&lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;&lt;version&gt;3.1.1&lt;/version&gt;&lt;/dependency&gt;&lt;!--shardingspherestart--&gt;&lt;!--forspringboot--&gt;&lt;dependency&gt;&lt;groupId&gt;io.shardingsphere&lt;/groupId&gt;&lt;artifactId&gt;sharding-jdbc-spring-boot-starter&lt;/artifactId&gt;&lt;version&gt;3.1.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--forspringnamespace--&gt;&lt;dependency&gt;&lt;groupId&gt;io.shardingsphere&lt;/groupId&gt;&lt;artifactId&gt;sharding-jdbc-spring-namespace&lt;/artifactId&gt;&lt;version&gt;3.1.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--shardingsphereend--&gt;&lt;!--lombok--&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;build&gt;&lt;plugins&gt;&lt;plugin&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;/project&gt;实体类以书本为例packagecom.macky.springbootshardingjdbc.entity;importcom.baomidou.mybatisplus.annotation.TableName;importcom.baomidou.mybatisplus.extension.activerecord.Model;importgroovy.transform.EqualsAndHashCode;importlombok.Data;importlombok.experimental.Accessors;/***@authorMacky*@TitleclassBook*@Description:书籍是实体类*@date2019/7/1315:23*/@Data@EqualsAndHashCode(callSuper=true)@Accessors(chain=true)@TableName(&quot;book&quot;)publicclassBookextendsModel&lt;Book&gt;{privateintid;privateStringname;privateintcount;}开放保存和查询两个接口,代码如下:packagecom.macky.springbootshardingjdbc.controller;importcom.macky.springbootshardingjdbc.entity.Book;importcom.macky.springbootshardingjdbc.service.BookService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;importjava.util.List;/***@authorMacky*@TitleclassBookController*@Description:TODO*@date2019/7/1220:53*/@RestControllerpublicclassBookController{@AutowiredBookServicebookService;@RequestMapping(value=&quot;/book&quot;,method=RequestMethod.GET)publicList&lt;Book&gt;getItems(){returnbookService.getBookList();}@RequestMapping(value=&quot;/book&quot;,method=RequestMethod.POST)publicBooleansaveItem(Bookbook){returnbookService.save(book);}}BookServiceImpl.javapackagecom.macky.springbootshardingjdbc.service.impl;importcom.baomidou.mybatisplus.core.toolkit.Wrappers;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.macky.springbootshardingjdbc.entity.Book;importcom.macky.springbootshardingjdbc.mapper.BookMapper;importcom.macky.springbootshardingjdbc.service.BookService;importorg.springframework.stereotype.Service;importjava.util.List;/***@authorMacky*@TitleclassBookServiceImpl*@Description:TODO*@date2019/7/1220:47*/@ServicepublicclassBookServiceImplextendsServiceImpl&lt;BookMapper,Book&gt;implementsBookService{@OverridepublicList&lt;Book&gt;getBookList(){returnbaseMapper.selectList(Wrappers.&lt;Book&gt;lambdaQuery());}@Overridepublicbooleansave(Bookbook){returnsuper.save(book);}}BookMapper.javapackagecom.macky.springbootshardingjdbc.mapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom.macky.springbootshardingjdbc.entity.Book;/***@authorMacky*@TitleclassBookMapper*@Description:TODO*@date2019/7/1220:46*/publicinterfaceBookMapperextendsBaseMapper&lt;Book&gt;{}创建数据库表,DDL语句如下:#创建数据库表数据CREATEDATABASEIFNOTEXISTS`db0`;USE`db0`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;CREATEDATABASEIFNOTEXISTS`db1`;USE`db1`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;CREATEDATABASEIFNOTEXISTS`db2`;USE`db2`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;配置分库分表策略application.properties:#数据源db0,db1,db2sharding.jdbc.datasource.names=db0,db1,db2#第一个数据库sharding.jdbc.datasource.db0.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db0.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db0.jdbc-url=jdbc:mysql://localhost:3306/db0?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db0.username=rootsharding.jdbc.datasource.db0.password=Aa123456#第二个数据库sharding.jdbc.datasource.db1.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db1.username=rootsharding.jdbc.datasource.db1.password=Aa123456#第三个数据库sharding.jdbc.datasource.db2.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db2.jdbc-url=jdbc:mysql://localhost:3306/db2?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db2.username=rootsharding.jdbc.datasource.db2.password=Aa123456#水平拆分的数据库(表)配置分库+分表策略行表达式分片策略#分库策略sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=idsharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=db$-&gt;{id%3}#分表策略其中book为逻辑表分表主要取决于id行sharding.jdbc.config.sharding.tables.book.actual-data-nodes=db$-&gt;{0..2}.book_$-&gt;{0..2}sharding.jdbc.config.sharding.tables.book.table-strategy.inline.sharding-column=count#分片算法表达式sharding.jdbc.config.sharding.tables.book.table-strategy.inline.algorithm-expression=book_$-&gt;{count%3}#主键UUID18位数如果是分布式还要进行一个设置防止主键重复#sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id#打印执行的数据库以及语句sharding.jdbc.config.props..sql.show=truespring.main.allow-bean-definition-overriding=true#读写分离sharding.jdbc.datasource.dsmaster=接口测试使用postman示例:GET请求------&gt;http://localhost:8080/bookPOST请求:-------&gt;http://localhost:8080/book?id=1&amp;name=java编程思想&amp;count=8demo的github地址:https://github.com/Macky-He/spring-boot--shardingsphere-examples如各位觉得有帮助的话,还请给个star鼓励鼓励博主,谢谢!三、总结分库分表实现按照官方文档做一个demo是第一步,如需深入还需要研究源码,研究架构,研究思想;此文仅作为入门demo搭建指南,如需深入理解,还请移步至官方文档。参考资料1.官方文档:https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/usage/sharding/

Tags: JAVA
个人随笔
原 Vue + SpringBoot实现WebSocket通信

Vue+SpringBoot实现WebSocket通信服务端在SpringBoot项目中添加ServerEndpointExporterBean的方法@BeanpublicServerEndpointExporterexporter(){returnnewServerEndpointExporter();}创建WebSocket客户端管理类:WebSocketComponent.javapackagecn.coralcloud.blog.web.component;importcom.alibaba.fastjson.JSON;importorg.springframework.stereotype.Component;importjavax.websocket.*;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.Objects;importjava.util.concurrent.CopyOnWriteArraySet;/**@authorgeff@nameWebSocketComponent@description@date2019-12-1814:22/@ServerEndpoint(value=&quot;/websocket&quot;)@ComponentpublicclassWebSocketComponent{/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。/privatestaticintonlineCount=0;/**concurrent包的线程安全Set,用来存放每个客户端对应的CumWebSocket对象。/privatestaticCopyOnWriteArraySetwebSocketSet=newCopyOnWriteArraySet&lt;&gt;();/**与某个客户端的连接会话,需要通过它来给客户端发送数据/privateSessionsession;/**连接建立成功调用的方法@paramsessionsession/@OnOpenpublicvoidonOpen(Sessionsession){this.session=session;//加入set中webSocketSet.add(this);//添加在线人数addOnlineCount();System.out.println(&quot;新连接接入。当前在线人数为:&quot;+getOnlineCount());}/**连接关闭调用的方法/@OnClosepublicvoidonClose(){//从set中删除webSocketSet.remove(this);//在线数减1subOnlineCount();System.out.println(&quot;有连接关闭。当前在线人数为:&quot;+getOnlineCount());}/**收到客户端消息后调用@parammessagemessage@paramsessionsession/@OnMessagepublicvoidonMessage(Stringmessage,Sessionsession){System.out.println(&quot;客户端发送的消息:&quot;+message);sendAll(JSON.toJSONString(messageDTO),session.getId());}/**群发@parammessagemessage/privatestaticvoidsendAll(Stringmessage,StringsessionId){webSocketSet.forEach(item-&gt;{if(!item.session.getId().equals(sessionId)){//群发try{item.sendMessage(message);}catch(IOExceptione){e.printStackTrace();}}});}/**发生错误时调用@paramsessionsession@paramerrorerror/@OnErrorpublicvoidonError(Sessionsession,Throwableerror){System.out.println(&quot;----websocket-------有异常啦&quot;);error.printStackTrace();}/**减少在线人数/privatevoidsubOnlineCount(){WebSocketComponent.onlineCount--;}/**添加在线人数/privatevoidaddOnlineCount(){WebSocketComponent.onlineCount++;}/**当前在线人数@returnint/publicstaticsynchronizedintgetOnlineCount(){returnonlineCount;}/**发送信息@parammessagemessagethrowsIOException/publicvoidsendMessage(Stringmessage)throwsIOException{//获取session远程基本连接发送文本消息this.session.getBasicRemote().sendText(message);//this.session.getAsyncRemote().sendText(message);}@Overridepublicbooleanequals(Objecto){if(this==o){returntrue;}if(o==null||getClass()!=o.getClass()){returnfalse;}WebSocketComponentthat=(WebSocketComponent)o;returnObjects.equals(session,that.session);}@OverridepublicinthashCode(){returnObjects.hash(session);}}@ServerEndpoint注解标识当前WebSocket服务端endpoint地址,本文实际前端访问的ws地址为:ws://localhost:8080/websocket。至此,服务端工作完成。页面VUE端&lt;template&gt;&lt;el-cardv-loading=&quot;loading&quot;element-loading-spinner=&quot;el-icon-loading&quot;:body-style=&quot;{padding:&#39;5px&#39;,backgroundColor:&#39;#eee&#39;}&quot;class=&quot;socket-box&quot;shadow=&quot;hover&quot;&gt;&lt;divclass=&quot;socket-box__content&quot;:style=&quot;{height:(boxHeight-125)+&#39;px&#39;}&quot;id=&quot;socket-content&quot;&gt;&lt;divv-if=&quot;hasMore&quot;@click=&quot;loadMore&quot;class=&quot;load-more&quot;&gt;&lt;span&gt;加载更多&lt;/span&gt;&lt;/div&gt;&lt;divv-elsestyle=&quot;width:100%;text-align:center;font-size:12px&quot;&gt;没有更多了&lt;/div&gt;&lt;divclass=&quot;item&quot;v-for=&quot;minmessages&quot;:class=&quot;checkMe(m)?&#39;sender&#39;:&#39;&#39;&quot;&gt;&lt;divclass=&quot;slide&quot;&gt;&lt;divclass=&quot;avatar&quot;:style=&quot;{background:m.background}&quot;&gt;{{m.name.substring(0,1)}}&lt;/div&gt;&lt;divclass=&quot;meta&quot;&gt;&lt;divclass=&quot;name&quot;&gt;{{m.name}}&lt;/div&gt;&lt;divclass=&quot;date&quot;&gt;{{m.createTime|datetime}}&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;{{m.content}}&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;divclass=&quot;socket-box__footer&quot;&gt;&lt;el-form@submit.native.prevent&gt;&lt;el-form-item&gt;&lt;el-inputtype=&quot;textarea&quot;resize=&quot;none&quot;:rows=&quot;3&quot;:disabled=&quot;!connect&quot;:placeholder=&quot;connect?&#39;输入内容...&#39;:&#39;当前连接断开,请刷新重试!&#39;&quot;:clearable=&quot;true&quot;v-model=&quot;message&quot;@keydown.native.enter=&quot;submitMsgForm&quot;&gt;&lt;/el-input&gt;&lt;/el-form-item&gt;&lt;el-form-item&gt;&lt;el-button@click=&quot;sendMsg(message)&quot;:disabled=&quot;!connect&quot;style=&quot;width:100%&quot;type=&quot;primary&quot;size=&quot;small&quot;&gt;发送(Enter)&lt;/el-button&gt;&lt;/el-form-item&gt;&lt;/el-form&gt;&lt;/div&gt;&lt;/el-card&gt;&lt;/template&gt;&lt;script&gt;import{GET}from&quot;@/api&quot;;exportdefault{name:&quot;Chatroom&quot;,data(){return{messages:[],message:&#39;&#39;,//boxHeight:document.documentElement.clientHeight-85,hasMore:true,pager:{pageNo:1,pageSize:10,total:0},loading:false,connect:false}},props:{boxHeight:{type:Number,required:true}},methods:{submitMsgForm(event){if(event.shiftKey){return;}event.preventDefault();this.sendMsg(this.message)},checkMe(message){letuser=localStorage.getItem(&quot;socketUser&quot;);if(user){user=JSON.parse(user);returnuser.uid===message.uid}else{returnfalse;}},initWebSocket:function(){this.websock=newWebSocket(`ws://localhost:8080/websocket`);this.websock.onopen=this.websocketonopen;this.websock.onerror=this.websocketonerror;this.websock.onmessage=this.websocketonmessage;this.websock.onclose=this.websocketclose;constthat=this;that.loading=true;GET({url:&#39;/api/personal/web/message/socketData?pageNo=1&#39;,callback:res=&gt;{if(res.code===200){that.messages=res.data.messages;that.hasMore=res.data.messages.length===that.pager.pageSize;that.$nextTick(function(){document.getElementById(&quot;socket-content&quot;).scroll({top:document.getElementById(&quot;socket-content&quot;).scrollHeight,left:0,behavior:&#39;smooth&#39;})})}that.loading=false}})},sendMsg(data){if(/^\s*$/.test(data)){this.message=&#39;&#39;;return;}//发送时传入JSON(UID,昵称,内容)constlocal=localStorage.getItem(&quot;socketUser&quot;);if(local){constl=JSON.parse(local);this.send(l,data)}else{//弹框this.$prompt(&#39;首次发表,请输入昵称&#39;,&#39;提示&#39;,{confirmButtonText:&#39;确定&#39;,cancelButtonText:&#39;取消&#39;,}).then(({value})=&gt;{//随机生成UIDconstuid=this.randomVideoUuid(32,16);constform={uid:uid,name:value,background:`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255})`};localStorage.setItem(&quot;socketUser&quot;,JSON.stringify(form));this.send(form,data)}).catch(()=&gt;{this.$message({type:&#39;info&#39;,message:&#39;取消输入&#39;});});}},send(obj,data){obj.content=data;obj.createTime=newDate().getTime();this.websock.send(JSON.stringify(obj));this.message=&#39;&#39;;this.messages.push(obj);this.$nextTick(function(){document.getElementById(&quot;socket-content&quot;).scroll({top:document.getElementById(&quot;socket-content&quot;).scrollHeight,left:0,behavior:&#39;smooth&#39;})})},loadMore(){this.loading=true;constthat=this;if(this.hasMore){this.pager.pageNo+=1;GET({url:&#39;/api/personal/web/message/socketData?pageNo=&#39;+this.pager.pageNo,callback:res=&gt;{if(res.code===200){that.messages=[...res.data.messages,...that.messages];that.hasMore=res.data.messages.length&gt;=that.pager.pageSize;}that.loading=false;}})}},websocketonopen:function(e){console.log(&quot;WebSocket连接成功&quot;,e);this.connect=true;},websocketonerror:function(e){console.log(&quot;WebSocket连接发生错误&quot;);this.connect=false;},websocketonmessage:function(e){constda=JSON.parse(e.data);this.messages.push(da);this.$nextTick(function(){document.getElementById(&quot;socket-content&quot;).scroll({top:document.getElementById(&quot;socket-content&quot;).scrollHeight,left:0,behavior:&#39;smooth&#39;})})},websocketclose:function(e){console.log(&quot;connectionclosed(&quot;+e.code+&quot;)&quot;);},randomVideoUuid(len,radix){letchars=&#39;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#39;.split(&#39;&#39;);letuuid=[];radix=radix||chars.length;if(len){for(leti=0;i&lt;len;i++)uuid[i]=chars[0|Math.random()*radix];}else{letr;uuid[8]=uuid[13]=uuid[18]=uuid[23]=&#39;-&#39;;uuid[14]=&#39;4&#39;;for(leti=0;i&lt;36;i++){if(!uuid[i]){r=0|Math.random()*16;uuid[i]=chars[(i===19)?(r&amp;0x3)|0x8:r];}}}returnuuid.join(&#39;&#39;);},},mounted(){this.initWebSocket();}}&lt;/script&gt;本段代码为简单的Vue实现的网页聊天室代码,其中@/api为自己简单封装的JS函数,用户初次进入页面时会生成一个随机UID保存到localStorage中,在mounted周期中初始化websocket连接。本聊天室最终效果地址:https://web.coralcloud.cn/blog/message

工作日志
原 【基于ELK搭建日志分析系统】- Logstash安装

汇总节点安装Logstash。Logstash运行需要JDK环境,所以需要首先配置相应JAVA_HOME环境变量。下载与filebeat对应版本压缩包:官网地址根据操作系统选择对应的版本下载解压测试:与Filebeat配置连接,新建beat-logstash.conf:指定配置文件启动logstash(cd$LOGSTASH_HOME):bin/logstash-fconfig/beat-logstash.conf后台启动使用nohupnohup./bin/logstash-fconfig/beat-logstash.conf--config.reload.automatic&gt;/dev/null2&gt;&amp;1&amp;