Flutter开发Linux程序中文乱码解决

盲猜由于字体缺失导致乱码,于是我把字体加过去 下载微软雅黑字体,也可以直接从 Windows 里复制一个 pubspec.yaml 配置 1 2 3 4 5 flutter: fonts: - family: cn fonts: - asset: fonts/msyh.ttc main.dart 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, // 配置字体 fontFamily: 'cn' ), home: HomePage(), ); } }

2020/12/8
articleCard.readMore

Docker安装Nginx

docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 version: '3.1' services: nginx: restart: always image: nginx container_name: nginx ports: - 81:80 volumes: - ./conf/nginx.conf:/etc/nginx/nginx.conf - ./wwwroot:/usr/share/nginx/wwwroot docker-compose.yml 同级下新建 conf 、 wwwroot 文件夹 , wwwroot 里新建 index.html ,写个 hello 当测试页面 conf 里创建 nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # 配置虚拟主机 192.168.80.144 server { # 监听的端口 listen 80; # 虚拟主机名称这里配置ip地址 server_name 192.168.80.144; # 所有的请求都以 / 开始,所有的请求都可以匹配此 location location / { # 使用 root 指令指定虚拟主机目录即网页存放目录 # 比如访问 http://ip/index.html 将找到 /usr/local/docker/nginx/wwwroot/html80/index.html # 比如访问 http://ip/item/index.html 将找到 /usr/local/docker/nginx/wwwroot/html80/item/index.html root /usr/share/nginx/wwwroot; # 指定欢迎页面,按从左到右顺序查找 index index.html index.htm; } } } 运行 docker-compose up -d 浏览器输入 http://192.168.80.144:81

2020/5/14
articleCard.readMore

flutter loading 动画插件(flutter_spinkit 4.1.2)

flutter_spinkit 4.1.2 pubspec.yaml 中添加依赖 1 2 dependencies: flutter_spinkit: "^4.1.2" 阅读文档,看看案例。照着实现一个最简单的例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 void main() => runApp(App()); class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'loading 动画', debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.dark, ), home: Scaffold( // SafeArea 异形屏适配 body: SafeArea( // 层叠布局 child: Stack( children: <Widget>[ Positioned.fill( child: Container( decoration: BoxDecoration(color: Colors.white), ), ), Positioned.fill( child: Container( // color: Colors.blueAccent, color: Colors.transparent, child: SpinKitFadingCircle( // 可以画一个圈,分为 0 1 2 3 4 5 6 7 8 9 份 itemBuilder: (_, int index) { return DecoratedBox( decoration: BoxDecoration( // isEven 表示是否整数 color: index.isEven ? Colors.red : Colors.green, ), child: Text("$index"), ); }, size: 120.0, ), ), ) ], ), ), ), ); } } 模拟一个简单的场景,点击登录出现 loading 动画,一段时间后取消 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 void main() => runApp(App()); class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'loading 动画', debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.dark, ), home: Scaffold( // SafeArea 异形屏适配 body: BodyWidget(), ), ); } } class BodyWidget extends StatefulWidget { @override _BodyWidgetState createState() => _BodyWidgetState(); } class _BodyWidgetState extends State<BodyWidget> { bool ifLoading = false; Timer timer; @override Widget build(BuildContext context) { return SafeArea( child: Stack( children: <Widget>[ Positioned.fill( child: Container( decoration: BoxDecoration(color: Colors.white), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ RaisedButton( child: Text("登录"), onPressed: () { print("登录啊"); setState(() { ifLoading = true; }); // 三秒后取消动画 timer = new Timer(Duration(seconds: 3), () { setState(() { ifLoading = false; }); }); }, ), ], ), ), ), ifLoading ? Positioned.fill( child: Container( color: Colors.blueAccent, child: SpinKitFadingCircle( // 可以画一个圈,分为 0 1 2 3 4 5 6 7 8 9 份 itemBuilder: (_, int index) { return DecoratedBox( decoration: BoxDecoration( // isEven 表示是否整数 color: index.isEven ? Colors.red : Colors.green, ), child: Text("$index"), ); }, size: 80.0, ), ), ) : Container(), ], ), ); } } 引入 provider 模拟更加真实的例子 1 2 3 dependencies: flutter_spinkit: "^4.1.2" provider: ^4.0.4 看到的效果是一样的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 void main() => runApp(App()); class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'loading 动画', debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.dark, ), home: Scaffold( // SafeArea 异形屏适配 body: MultiProvider( providers: [ ChangeNotifierProvider( create: (_) => LoginProvider(), ) ], child: Consumer<LoginProvider>(builder: (context,loginProvider,_){ return BodyWidget(ifLoading: loginProvider.ifLoading,onPressed: loginProvider.onPressed,); },), ), ), ); } } class LoginProvider with ChangeNotifier { bool ifLoading = false; VoidCallback onPressed; Timer timer; LoginProvider() { onPressed = () { ifLoading = true; // notifyListeners 我的理解就是把它当做 setState 用 notifyListeners(); // 模拟发送网络请求异步获得请求结果 timer = new Timer(Duration(seconds: 3), () { ifLoading = false; notifyListeners(); }); }; } } class BodyWidget extends StatelessWidget { bool ifLoading = false; VoidCallback onPressed; BodyWidget({@required this.ifLoading, @required this.onPressed}); @override Widget build(BuildContext context) { return SafeArea( child: Stack( children: <Widget>[ Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, decoration: BoxDecoration(color: Colors.amberAccent), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ RaisedButton( child: Text("登录"), onPressed: onPressed, ), ], ), ), ifLoading ? Positioned.fill( child: Container( color: Colors.blueAccent, child: SpinKitFadingCircle( // 可以画一个圈,分为 0 1 2 3 4 5 6 7 8 9 份 itemBuilder: (_, int index) { return DecoratedBox( decoration: BoxDecoration( // isEven 表示是否整数 color: index.isEven ? Colors.red : Colors.green, ), child: Text("$index"), ); }, size: 120.0, ), ), ) : Container(), ], ), ); } }

2020/3/29
articleCard.readMore

关于使用@CrossOrigin遇见的的一些问题和理解

记录今天遇到的一个问题 个人理解 参考需谨慎。如果有错误,谢大佬指点 是关于跨域的,在昨天写项目的时候遇到的跨域请求的问题,网上通常的配置方法都是使用 过滤器,或者 实现 WebMvcConfigurer 接口的方法。相对来说有些老了。不符合我想要的效果。 1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600) .allowedHeaders("*"); } } 然后我在阅读 Spring 官网 看到有使用 @CrossOrigin 方式可以只需要加一个注解就行(简单粗暴我喜欢)。于是我便采用的这种方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RestController @CrossOrigin(value = "*",allowCredentials = "true") @RequestMapping("user") public class UserController { @PostMapping(value = "login") public Map<String, Object> login(@RequestBody User user) { Map<String, Object> map = new HashMap<>(); map.put("userName",user.getUserName()); map.put("userPass",user.getUserPass()); return map; } @PostMapping(value = "logout") public Map logout(@RequestBody String token) { Map<String, String> map = new HashMap<>(); map.put("token",token); return map; } } 配置之后问题就来了在访问 login 接口的时候可以正常访问,但是 请求 logout接口的时候,还是出现的跨域的问题。 在反复检查配置,阅读官网后。尝试了上面的方法配置,完全没问题 (那我代码应该没问题 2333),但是换回 @CrossOrigin 依旧请求失败。 由于那种配置方法比较死,不是很灵活 (我不喜欢)。所以我想继续使用 @CrossOrigin 的方式配置。 于是便把部分代码抽取出来,单独建一个干净的项目复现刚才的问题。 按照 Spring 官网的配置方法一路通畅。于是对比这个 demo 和我原先的 项目,便发现了原因 !!! 因为我配置了一个登录拦截器,用于验证 token 。拦截器不拦截 login 方法。拦截需要验证 token 的方法,所以就出现了部分有效,部分无效的问题。 所有经过了拦截器的方法都无法访问。 好,知道了问题的所在,让我继续翻文档。。。。 了解到。@CrossOrigin 这个注解本质也是一个拦截器,往response里添加 Access-Control-Allow-Origin 等响应头信息。 如果在 controller 类上或在方法上标了 @CrossOrigin 注解,则 spring 在记录 mapping 映射时会记录对应跨域请求映射。 当一个跨域请求过来时,spring 在获取 handler 时会判断这个请求是否是一个跨域请求,如果是,则会返回一个可以处理跨域的 handler QAQ 这时候找到 HandlerMapping 里的 getCorsHandlerExecutionChain方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, @Nullable CorsConfiguration config) { if (CorsUtils.isPreFlightRequest(request)) { HandlerInterceptor[] interceptors = chain.getInterceptors(); chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors); } else { // 写死了 index 0 emmmm chain.addInterceptor(0, new CorsInterceptor(config)); } return chain; } 找到 CorsInterceptor的 preHandle打上断电点,和自己的拦截器打上断点(自己的先执行,凉凉) 好像 Spring 也没给配置拦截器执行顺序的东东(菜鸡的我不知道) 到此可以知道,@CrossOrigin 是在 spring 映射 handler 的时候 干的活。那既然拦截器不行,那我就用 Spring AOP 吧 QWQ !!! 或者在拦截器中设置,只要是预请求(OPTIONS)全部直接放行

2019/12/6
articleCard.readMore

Linux 常用指令、IntelliJ IDEA 常用快捷键

Linux 篇 查询占用此端口的程序 。 lsof -i:{port} 。 例:lsof -i:3306 。 netstat -tunlp|grep {port} 。 例:netstat -tunlp|grep 8080 。 查看端口使用情况 netstat -anp 。 查看本机资源使用情况 top 或安装(apt-get install htop)htop可实时变化 。 杀死进程 kill {pid} 。例:kill 7821 。 查进程 ps aux | grep *** 。 查日志 tail -f catalina.out 。 打 tar 包 tar zcvf FileName.tar.gz DirName 。 解压 tar 包 tar zxvf FileName.tar.gz 。 修改 hostname hostnamectl set-hostname you-hostname 查看网关地址 route -n 配置 hosts sudo vim /etc/hosts ubuntu18.04 取消 dhcp ,修改 ip 地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vi /etc/netplan/50-cloud-init.yaml network: ethernets: ens33: addresses: [192.168.80.160/24] gateway4: 192.168.80.2 nameservers: addresses: [114.114.114.114,8.8.8.8] version: 2 # 使配置生效 netplan apply 查日志常用 less 的一些操作 1 2 3 4 5 6 # `G` 到日志末尾 # `?` 往上面查找 # `/` 往下面查找 # `n` 下一个匹配项 # `N` 上一个匹配项 # `j/k` 上一行/ 下一行 修改系统环境变量 sudo vim /etc/proflie root 账户下使用其他用户运行程序 sudo -u username /usr/bin/appname 添加应用图标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 sudo vim /usr/share/applications # 复制一个原来的图标,对照修改 # 例:system-config-printer.desktop [Desktop Entry] Name=Printers # 名字 GenericName=Printers X-GNOME-FullName=Printers Comment=Configure printers Exec=system-config-printer # 执行的程序 Terminal=false Type=Application Icon=printer # 图标 StartupNotify=true NotShowIn=KDE;GNOME; X-Ubuntu-Gettext-Domain=system-config-printer Categories=GNOME;GTK;Settings;HardwareSettings;X-GNOME-Settings-Panel;X-Unity-Settings-Panel;System;Printing; # 分类 X-GNOME-Settings-Panel=printing X-Unity-Settings-Panel=printing Keywords=Printer;Queue;Print;Paper;Ink;Toner; X-Desktop-File-Install-Version=0.24 Maven,Flutter,Java 等系统环境变量配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vim /etc/profile export JAVA_HOME=/usr/local/java/jdk1.8.0_171 export JRE_HOME=/usr/local/java/jdk1.8.0_171/jre export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$HOME/bin export M2_HOME=/usr/local/apache-maven-3.6.3 export PATH=${M2_HOME}/bin:$PATH export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn export FLUTTER_HOME=/usr/local/flutter export PATH=${PATH}:${FLUTTER_HOME}/bin 查看当前目录已经占用了多大空间,需要进入目录后输入命令 du -sh IntelliJ IDEA 篇 万能快捷键 Alt+Enter 。 XML 或者 HTML 中选中一个节点 Ctrl+W 。 删除光标所在行 Ctrl+Y 。 双击 Shift 搜索 。 重命名 Ctrl+Shift+R 。 返回上次查看代码的位置, Alt+←、 Alt+→ 。 折叠/ 展开代码块的快捷键 。 Ctrl + +/- , 当前方法展开、折叠 。 Ctrl + Shift + +/- , 全部展开、折叠 。 IDEA 内切换窗口 Ctrl + Alt + [/] 关闭当前打开的文件选项卡 Ctrl + F4 向下复制一行 Ctrl + D

2019/11/17
articleCard.readMore

了解23种设计模式-单例模式

饿汉式 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Mgr01 { private static final Mgr01 INSTANCE = new Mgr01(); private Mgr01(){} private static Mgr01 getInstance(){return INSTANCE;} public void m(){ System.out.println("m"); } public static void main(String[] args) { Mgr01 m1 = Mgr01.getInstance(); Mgr01 m2 = Mgr01.getInstance(); System.out.println(m1 == m2); } } 类加载到内存后就实例化一个单例 , JVM 保证线程安全 缺点: 不管是否用到,类装载时就完成实例化 (通常问题不大,写法简单) 懒汉式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Mgr02 { private volatile static Mgr02 INSTANCE; private Mgr02(){}; /** * 可以使用线程锁的方式(synchronized 修饰此方法)解决,但是性能会下降 * @return */ public static Mgr02 getInstance(){ if (INSTANCE == null) { // 或者在此处加锁,解决线程安全问题 synchronized (Mgr02.class){ if (INSTANCE == null ){ INSTANCE = new Mgr02(); } } } return INSTANCE; } public void m(){ System.out.println("m"); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ // 同一个类的 不同对象,它的 hashCode 是不同的 System.out.println(Mgr02.getInstance().hashCode()); }).start(); } } } 为了保证线程安全,写法相对饿汉式麻烦,但是只有在使用时才会实例化 静态内部类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Mgr03 { private Mgr03(){} private static class Mgr03Holder { private static final Mgr03 INSTANCE = new Mgr03(); } public static Mgr03 getInstance() { return Mgr03Holder.INSTANCE; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ // 同一个类的 不同对象,它的 hashCode 是不同的 System.out.println(Mgr03.getInstance().hashCode()); }).start(); } } } 与饿汉式类似,都是由 JVM 保证线程安全,但静态内部类是延迟加载 枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public enum Mgr04 { //枚举单例 INSTANCE; public void m(){ System.out.println("m"); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ // 同一个类的 不同对象,它的 hashCode 是不同的 System.out.println(Mgr04.INSTANCE.hashCode()); }).start(); } } } 枚举单例,解决线程同步,解决反序列化(不能通过反射拿到多个实例,枚举没有构造方法)

2019/11/5
articleCard.readMore

Spring Boot 邮件服务提供者

依赖 pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>online.cccccc.test</groupId> <artifactId>email</artifactId> <version>0.0.1-SNAPSHOT</version> <name>email</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <licenses> <license> <name>Apache 2.0</name> <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> </license> </licenses> <developers> <developer> <id>cqiang</id> <name>沐凉</name> <email>cqiang102@foxmail.com</email> </developer> </developers> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>online.cccccc.test.email.EmailApplication</mainClass> </configuration> </plugin> </plugins> </build> </project> Spring boot 配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 spring: application: name: provider-mail mail: host: smtp.qq.com # 你的邮箱授权码 password: properties: mail: smtp: auth: true starttls: enable: true required: true # 发送邮件的邮箱地址 username: thymeleaf: cache: false # 开发时关闭缓存,不然没法看到实时页面 mode: HTML # 用非严格的 HTML encoding: UTF-8 servlet: content-type: text/html # 服务端口号 server: port: 82 测试代码 @Async 使用异步,需要在Spring boot 启动类添加 @EnableAsync Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * @author 你是电脑 * @create 2019/11/3 - 12:45 */ @RestController public class EmailController { @Resource private EmailService emailService; @PostMapping("email") public String sendEmail(@RequestBody EmailVo emailVo){ emailService.receive(emailVo.getBody(),emailVo.getTo()); return "发送成功"; } } Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 /** * @author 你是电脑 * @create 2019/11/3 - 12:44 */ @Service public class EmailService { @Resource private ConfigurableApplicationContext applicationContext; @Resource private JavaMailSender javaMailSender; @Resource private TemplateEngine templateEngine; @Async public void receive(String body,String to) { Context context = new Context(); //往 模板中塞数据 context.setVariable("test",body) ; String emailTemplate = templateEngine.process("index", context); sendTemplateEmail("支付宝到账100万元", emailTemplate, to); } /** * 发送普通邮件 * @param subject * @param body * @param to */ @Async public void sendEmail(String subject, String body, String to) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(applicationContext.getEnvironment().getProperty("spring.mail.username")); message.setTo(to); message.setSubject(subject); message.setText(body); javaMailSender.send(message); } /** * 发送 HTML 模板邮件 * @param subject * @param body * @param to */ @Async public void sendTemplateEmail(String subject, String body, String to) { MimeMessage message = javaMailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(message, true); String property = applicationContext.getEnvironment().getProperty("spring.mail.username"); assert property != null; helper.setFrom(property); helper.setTo(to); helper.setSubject(subject); helper.setText(body, true); javaMailSender.send(message); } catch (Exception e) { } } } 模板文件 1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>你有一封新邮件</title> </head> <body> <h2>测试:<span th:text="${test}"></span></h2> </body> </html> 使用 Postman 测试接口

2019/11/3
articleCard.readMore

Docker 搭建redis单节点以及一主两从三哨兵模式

redis 单节点部署 docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 version: '3.1' services: redis: image: redis container_name: my_redis restart: always command: redis-server --requirepass your_password ports: - 6379:6379 volumes: - ./data:/data 使用Spring boot 连接配置application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: application: name: porject-name redis: database: 0 host: 127.0.0.1 port: 6379 password: 123456 timeout: 200 jedis: pool: max-idle: 8 min-idle: 0 max-wait: -1 max-active: 8 redis 集群部署 一主两从三哨兵 一主两从 docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 version: '3.7' services: master: image: redis container_name: redis-master restart: always command: redis-server --port 6379 --requirepass your_password --appendonly yes ports: - 6379:6379 volumes: - ./data:/data slave1: image: redis container_name: redis-slave-1 restart: always command: redis-server --slaveof redis-master 6379 --requirepass your_password --masterauth your_password --appendonly yes ports: - 6380:6379 volumes: - ./data:/data slave2: image: redis container_name: redis-slave-2 restart: always command: redis-server --slaveof redis-master 6379 --requirepass your_password --masterauth your_password --appendonly yes ports: - 6381:6379 volumes: - ./data:/data 检查 redis 是否部署成功 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 使用此命令进入docker 容器 docker exec -it redis-master bash # 使用redis 客户端连接redis redis-cli # 认证 auth your_password # 添加一条数据 set test1 test # 再取出来 get test1 # 操作全部没问题就可以退出 master exit # 在以同样的方式进入 从服务器 测试是否可以拿到刚添加的数据 且不能使用 set 添加数据,说明 一主两从搭建成功 get test1 三哨兵 docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 version: '3.7' services: sentinel1: image: redis container_name: redis-sentinel-1 command: redis-sentinel /usr/local/etc/redis/sentinel.conf restart: always ports: - 26379:26379 volumes: - ./sentinel1.conf:/usr/local/etc/redis/sentinel.conf sentinel2: image: redis container_name: redis-sentinel-2 command: redis-sentinel /usr/local/etc/redis/sentinel.conf restart: always ports: - 26380:26379 volumes: - ./sentinel2.conf:/usr/local/etc/redis/sentinel.conf sentinel3: image: redis container_name: redis-sentinel-3 command: redis-sentinel /usr/local/etc/redis/sentinel.conf restart: always ports: - 26381:26379 volumes: - ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf 三哨兵 配置文件 sentinel.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 port 26379 dir /tmp # 自定义集群名,其中 192.168.8.188 为 redis-master 的 ip,6379 为 redis-master 的端口,2 为最小投票数(因为有 3 台 Sentinel 所以可以设置成 2) sentinel monitor mymaster 192.168.8.188 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster your_password sentinel failover-timeout mymaster 180000 sentinel deny-scripts-reconfig yes # 复制三份 cp sentinel.conf sentinel1.conf cp sentinel.conf sentinel2.conf cp sentinel.conf sentinel3.conf 检查是否可用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 进入sentinel docker exec -it redis-sentinel-1 bash # 连接 redis-sentinel redis-cli -p 26379 # 查看从节点状态 sentinel slaves mymaster # 查看到每个从节点都连接到主节点,就是正常的 # 31) "master-link-status" # 32) "ok" # 可以尝试把主节点停止,可以观察sentinel日志,发现sentinel 会重新选举主节点 docker-compose logs -f docker stop redis-master 使用Spring boot 连接配置application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 spring: application: name: porject-name redis: database: 0 password: your_password timeout: 200 jedis: pool: # 连接池中的最大空闲连接 max-idle: 8 # 连接池中的最小空闲连接 min-idle: 0 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1 # 连接池最大连接数,默认8个,(使用负值表示没有限制) max-active: 8 sentinel: master: mymaster nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381

2019/10/24
articleCard.readMore

Hello World

郑重的写下Hello World

2019/10/17
articleCard.readMore

Ubuntu Server 安装 Java、Tomcat、mysql

安装Java 使用xftp把压缩包上传到Ubuntu 中,我这里使用的是JDK1.8 https://www.oracle.com/technetwork/java/javase/downloads/index.html 解压缩 1 tar -zxvf jdk-8u152-linux-x64.tar.gz 创建目录 1 mkdir -p /usr/local/java 移动解压好的安装包 1 mv jdk1.8.0_152/ /usr/local/java/ 设置所有者 1 chown -R root:root /usr/local/java/ 配置系统环境变量 1 2 3 4 5 6 vim /etc/environment 在第二行加入 export JAVA_HOME=/usr/local/java/jdk1.8.0_152 export JRE_HOME=/usr/local/java/jdk1.8.0_152/jre export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib 配置用户环境变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /etc/profile 修改为如下 if [ "$PS1" ]; then if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then # The file bash.bashrc already sets the default PS1. # PS1='\h:\w\$ ' if [ -f /etc/bash.bashrc ]; then . /etc/bash.bashrc fi else if [ "`id -u`" -eq 0 ]; then PS1='# ' else PS1='$ ' fi fi fi #加入的语句 export JAVA_HOME=/usr/local/java/jdk1.8.0_152 export JRE_HOME=/usr/local/java/jdk1.8.0_152/jre export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$HOME/bin #加入的语句 if [ -d /etc/profile.d ]; then for i in /etc/profile.d/*.sh; do if [ -r $i ]; then . $i fi done unset i fi 使环境变量生效 1 source /etc/profile 测试安装是否成功 1 java -version 安装Tomcat 我这里安装的是Tomcat 8.5.23 https://tomcat.apache.org/ 解压缩上传的压缩文件 1 tar -zxvf apache-tomcat-8.5.23.tar.gz 改成简短的文件名 1 mv apache-tomcat-8.5.23 tomcat 移动目录 1 mv tomcat/ /usr/local/ 启动 执行tomcat/bin/目录下的startup.sh 安装MySql 更新数据源 1 apt-get update 安装 1 2 3 apt-get install mysql-server #安装途中需要设置root密码 如遇到如下问题,则执行划线部分代码 E: dpkg was interrupted, you must manually run ‘++dpkg –configure -a++’ to correct the problem 配置允许远程连接 1 2 3 4 vim /etc/mysql/mysql.conf.d/mysqld.cnf #注释掉如下行(前面加上#号) bind-address = 127.0.0.1 重启Mysql服务 1 service mysql restart 授权 root 用户允许所有人连接 1 grant all privileges on *.* to 'root'@'%' identified by '你的 mysql root 账户密码'; 如果出现如下情况,是由于密码安全级别太低 1 2 3 4 5 6 ERROR 1819 (HY000): Your password does not satisfy the current policy requirements 使用如下设置即可 set global validate_password_policy=0; //设置密码安全级别 set global validate_password_length=1; //设置密码长度限制 重启Mysql服务 1 service mysql restart

2019/6/27
articleCard.readMore