PG索引心得

高数据量下(500w数据)查询条件有两个字段,对两个字段加联合索引,速度比分别加索引要快。

条件若是字段比较大小,若该字段是字符串类型,则速度较慢,若改为big integer类型(可以parse long),则速度可提升80%左右(原1.6s,现230ms)。

心得:

1、查询条件的字段建联合索引;

2、查询条件的字段如果是比较大小,用整型会比字符串类型速度快。

高并发SqlSession close前新开SqlSession导致阻塞问题

最近测试程序的高并发,发现高并发下卡死,百分之百复现。经过一天的调查,最终定位问题所在。

程序结构为:

SqlSession session = DbUtil.getSession();
try{
...

    SqlSession session2 = DbUtil.getSession();
    try{

    ...
    
    } finally {
        session2.close();
    }

...

} finally {
    session.close();
}

当高并发访问时,session申请掉了所有的数据库连接,当进入到session2时,无法获取到新的数据库连接,阻塞,导致session无法执行到close,没有新的连接可用,整个程序无法获取新的数据库连接。

可以尝试减少服务的最大线程数,增加数据库的最大连接数,使得数据库连接数大于服务线程数。但是这样治标不治本。

能想到两个办法:

1、将这种session内申请session的都修改要么移到session外,要么使用前面的session。也就是从代码逻辑上规避。

2、设置Druid的MaxWait,例如设置为2000(毫秒)

参考:

Druid数据库连接池配置(案例及排查指南)

https://tech.youzan.com/shu-ju-ku-lian-jie-chi-pei-zhi/

更新:

设置Druid的removeAbandoned,removeAbandonedTimeout将超过20秒的连接停止掉。

设置logAbandoned打印出来abandoned的连接。

https://www.cnblogs.com/spec-dog/p/6226212.html

总结:

1、修改代码,将所有事务内嵌套事务改为单线事务
2、优化代码,对于查数据库能缓存的做缓存处理;优化sql
3、尝试不同的Druid连接池配置及ICE最大线程数,寻找较优组合
4、配置Druid的removeAbandoned,释放长时间未完成的连接
5、更新Druid、pg库
6、尝试将Druid更换为HiKariCP,未见改善,改回Druid

MyBatis事务相关心得

使用过程中发现,对某条记录update,如果session未commit,则其他session对该记录无法写(会阻塞),但是对别的记录是可以写的。感觉是行级别的锁。

如果设置隔离级别为Serializable,则就算是对该表其他记录操作也是抛异常的,感觉是表级别的锁。

但是几个隔离级别还是不懂。

autoCommit=false,没有执行commit(),仅执行close(),会发生什么

困扰许久的问题终于找到了答案:

结论:autoCommit=false,但是没有手动commit,在sqlSession.close()时,Mybatis会将事务进行rollback()操作,然后才执行conn.close()关闭连接。

我的理解:在autoCommit = false的情况下,不commit,事务是不会提交的,就算不调用rollback,也是可以的。

https://my.oschina.net/zudajun/blog/666764

Dart Java Swift AES CBC PKCS7Padding

使用Flutter开发,加密必不可少。

可选方案两种:1、Dart实现;2、原生实现。

既然要跨平台,那当然尽量用Dart实现,不然将来如果需要支持桌面、web还需要新的原生实现,麻烦。

试了pub上的几个高分的库,加密的结果和java都不一样。而且相关参数也不明所以。所以又不得不研究一下AES。

AES加密算法浅析

https://mp.weixin.qq.com/s/Q99jGZOUGFiM-ZTnkWWYew

这篇文章比较易懂,读完大致了解了AES算法,对几个要素也明确了。

秘钥

秘钥长度决定了加密的轮数,分为128bit,192bit,256bit,128性能最好,256安全性最高。

暂时选定的128bit。

填充(Padding)

AES是分组加密,每一组(块)是128bit,16字节,所以明文必须是16字节的整数倍。如果不是,则需要Padding。

大致有三种Padding:NoPadding、PKCS7Padding、ISO10126Padding。

这几种Padding好像是业界标准,不止可以用在AES,别的加密算法也可以使用。

NoPadding就是不用Padding,但是需要保证加密明文的长度是16字节的整数倍。

PKCS7Padding、ISO10126Padding是按照某种规则填充到16字节的整数倍。

暂时选定PKCS7Padding。

模式(mode of operation)

主要关注ECB、CBC这两种模式。

ECB:每个明文块加密独立完成,互不干涉。优点是各个块可以并行计算,缺点是安全性差。

CBC:引入初始向量(IV),初始向量会参与第一个明文块的异或,后续的每个明文块会和前一个明文块的秘闻块异或。由于不是互不干涉,所以无法并行计算,优点是更安全。

所以可以看出,IV的长度和明文块的长度是一样的,16字节(128bit)

关于模式,可以参看wiki

https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

选定CBC。

Java实现

Java作为非常成熟的语言,库应有尽有。但是还是卡壳了。

使用时发现,Java没有PKCS7Padding,只有PKCS5Padding。

经过一番查找资料,发现资料非常少,有限的解决方案里发现了可以使用bouncycastle的库来实现。

https://blog.csdn.net/ngh8897/article/details/46008467

结果使用时发现,首次初始化的时候非常非常慢,大概1秒多。

于是继续在网上查找。

https://stackoverflow.com/questions/29232705/encrypt-text-to-aes-cbc-pkcs7padding

https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding

总结起来就是,AES根本没有PKCS5Padding

PKCS5Padding、PKCS7Padding的区别就是,block size。PKCS5Padding定义的是8byte的block size,PKCS7Padding可以是1-255bytes。而AES的block是16byte,所以PKCS5Padding根本不能用在AES上。

Java官方文档似乎也没给出确定的文字。从网上的有限的资料来看,Java中PKCS5Padding就是PKCS7Padding,历史遗留,没有改名字。所以也不用引入三方库了,参数使用AES/CBC/PKCS5Padding即可。

Dart实现

在pub.dev上搜AES,试遍了几个高分库,加密结果都和Java不一致,而且和几个在线AES加密网站的加密结果也不一致,不知道什么原因。

几个原生交互的库倒是加密结果一致的,但是想的是尽量用纯dart的。就在要放弃,考虑选择别的加密方式时,看到一篇文章,他们使用的是pointycastle。我在pub.dev上看了一下,此库只有64分。抱着试试看的态度,试了一下,加密结果一致。于是选定了pointycastle来AES加密。

参考:

http://guoyz.top/2019/05/03/Flutter%E5%8A%A0%E8%A7%A3%E5%AF%86%E4%B9%8B%E6%AE%87/

经大神指出,pub上的高分AES是正确的,是我没用对。

库名称为encrypt。

我没算对是因为mode没赋值,默认的是sic。感谢大神。

    final key = Encrypt.Key.fromBase64(keyString);
    final iv = Encrypt.IV.fromBase64(ivString);


    final encrypter = Encrypt.Encrypter(Encrypt.AES(key, mode: Encrypt.AESMode.cbc));

    final encrypted = encrypter.encrypt(plainText, iv: iv);
    final decrypted = encrypter.decrypt(encrypted, iv: iv);

Swift实现

待续..

Flutter学习笔记

Dart

https://book.flutterchina.club/chapter1/dart.html

Flutter页面切换动画设为iOS风格,且支边缘持滑动返回

MaterialApp(theme: ThemeData(platform: TargetPlatform.iOS)

发现了其他解决方案:

不改变theme,在跳转页面时,调用:

                Navigator.push( context,
                    CupertinoPageRoute(builder: (context) {
                      return NewRoute();
                    }));

而不是:

                Navigator.push( context,
                    MaterialPageRoute(builder: (context) {
                      return NewRoute();
                    }));

这样在Android设备上,页面跳转动画也和iOS一样,且可以滑动返回。

如果设置跳转为MaterialPageRoute,在Android设备上是Material风格,但是在iOS上依然是iOS风格。

关于类似Android中style.xml的写法问题

Android中可以再style.xml中定义某种常用控件的样式,简化代码,也便于修改。

但是查找了很多资料,好像Flutter中暂时没有此类机制。

Text可以使用ThemeData中的textTheme类似地实现,其他控件暂时没发现解决办法。

textTheme规定了(好像是)13种text的样式。

https://flutter.dev/docs/cookbook/design/themes

https://api.flutter.dev/flutter/material/TextTheme-class.html

https://medium.com/flutter-community/flutter-apply-style-as-a-theme-in-a-text-widget-90268328bd23

merge也挺有用,使用方法:

Theme.of(context)
.textTheme
.title
.merge(TextStyle(color: Colors.red)

P.S.不必修改textTheme。可以创建一个类,单独存储TextStyle,Text直接在此引用。

Android原生交互相关问题

在Android目录下打开新的Android Studio编辑(例如打开build.gradle,点击Open for Editing in Android Studio)

https://stackoverflow.com/questions/51544535/how-to-import-third-party-android-library-to-java-file-in-flutter

国际化

感觉非常麻烦,参考:

https://book.flutterchina.club/chapter13/intl.html

https://www.jianshu.com/p/6ca24dd50c57

http://w4mxl.github.io/2019/01/26/flutter-Intl-Localizations/

添加依赖:

dependencies:
  flutter_localizations:
    sdk: flutter

生成完成后,使用:

    return MaterialApp(
      localizationsDelegates: [
        LocalizationStringDelegate() // 设置Delegate
      ],
      supportedLocales: [
        const Locale('en', ''), // 英语
        const Locale('zh', 'CN'), // 中文简体
        //其它Locales
      ],
// ...


LocalizationString.of(context).home

P.S.国际化可以使用Flutter i18n插件,非常方便

https://www.jianshu.com/p/b9f830efe1f8

依赖不变,依然是flutter_localizations。插件安装后重启Android Studio,工程会自动增加了lib/generated/,res/values/。直接编辑arb文件即可,自动生成其他dart文件。创建arb时选择语言、地区。

      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        S.delegate,
      ],
      supportedLocales: S.delegate.supportedLocales,

S.of(context).home

关于和Android异步对比

https://www.jianshu.com/p/8c422130e750

还需细细研读。

Dart AES CBC PKCS7Padding

这两天一直研究加密的事了,我觉得应该单开一篇文章。说开就开。

TextEditingController销毁问题

页面退出时,TextEditingController应该销毁。

Remember to dispose of the TextEditingController when it is no longer needed. This will ensure we discard any resources used by the object.

https://api.flutter.dev/flutter/widgets/TextEditingController-class.html

https://flutter.dev/docs/cookbook/forms/text-field-changes

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the
    // widget tree.
    myController.dispose();
    super.dispose();
  }

Tab切换不重新加载页面

https://www.jianshu.com/p/3773a882ab0c

ClipRRect

可以使用ClipRRect做出圆角Image

Hero

Hero可以在切换页面时,将某控件飞到下个页面

FittedBox

FittedBox可以调整子控件拉抻、平铺、裁剪等等

今天开始入坑Flutter辣

今天开始入坑Flutter,不知Flutter未来前景如何,也不知自己能否学成,希望有个好结果叭。加油鸭。

章鱼烧心得

几个月不更博客,突然画风一变,变成了美食博主。

低筋面粉与水大概是100克 : 150ml这样,100克可以放1个鸡蛋。今天大概是200克低筋面粉+350ml水+2鸡蛋。酱油酌情放,盐酌情放。

先大火把锅烧热,放油,油可以多点,多点就不沾,涂匀,等到挺热了再倒入面糊。

然后就可以马上放入章鱼、疙瘩白、圆葱了。

持续小火加热,等到可以翻动了再翻,不然翻碎了。天然气还是太热,糊得快。翻过来再继续加热,再加热到原来冲上的那面也大概变圆了,就差不多了,就可以把火关了用余热加热了。

倒上照烧汁、芥末酱、番茄酱、蛋黄酱、木鱼花,就可以吃辣。

所有材料(落下了鸡蛋)
第一次做的时候油没烧开,面太稀了
之后就越来越熟练了,但还是会糊
成品,好吃

iOS开发踩坑记录

开屏页无法显示图片问题

LaunchScreen.storyboard设置Assets.xcassets里的图片无法显示。解决办法是把图片放到外面,工程目录下建个文件夹,把图片放里,而不是在Assets.xcassets里。

后来发现其实不是这个问题,把图片放在外面有时候也会不显示。重启手机再重新build安装就会显示。无论是放在Assets.xcassets里还是外面,都会显示。不知道是否和auto layout有关,把UIImageView设为水平居中、竖直居中、距上边距0、左边距0。

开屏页国际化问题

对LaunchScreen.storyboard国际化似乎不起作用,对LaunchScreen里显示的图片做国际化,似乎也没起作用。解决办法是创建需要本地化的LaunchScreen.storyboard,命名为LaunchScreenZhCN.stroyboard。修改InfoPlist.strings,对UILaunchStoryboardName国际化,值为对应的LaunchScreen名。

但是运行过程中发现,开屏页确实本地化了,但是切换语言,开屏页并没有变化。网上找到的解答是:

启动页只会保留一份, 也就是说, 你第一次加载完以后, 切换了语言, 再重新打开App, 它的启动页不会跟着更新的。 这也符合苹果的用户交互指引。

也就是说,开屏页的语言为安装APP时系统语言,其后更改系统语言也不会改变开屏页,除非重新安装。

参考:

https://www.jianshu.com/p/605978f28629

http://blog.hudongdong.com/ios/559.html

国际化思维导图
WKWebView使用问题

在storyboard里使用WKWebView会报错,提示Class Unavailable: WKWebView before iOS 11.0 (NSCoding support was broken in previous versions)

实际上在iOS8.0就开始支持WKWebView了,那为什么还会报错呢?

网上的解答是:

Although WKWebView was introduced in iOS 8, there was a bug in -[WKWebView initWithCoder:] that was only fixed in iOS 11, which always crashed at runtime and thus prevented configuring one within Interface Builder.

意思好像是虽然从iOS8开始支持,但是有个bug直到iOS11才修复,如果iOS11之前使用Interface Builder创建WKWebView会导致崩溃。

所以解决办法是通过代码创建WKWebView。

import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func loadView() {
        super.loadView()
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        let myURL = URL(string: "https://www.apple.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }}

参考:

https://stackoverflow.com/questions/46221577/xcode-9-gm-wkwebview-nscoding-support-was-broken-in-previous-versions

http支持

编辑info.plist,增加

	<key>NSAppTransportSecurity</key>
	<dict>
	    <key>NSAllowsArbitraryLoads</key>
	    <true/>
	    <key>NSAllowsArbitraryLoadsInWebContent</key>
  	    <true/>
	</dict>

参考:

https://stackoverflow.com/questions/41557155/wkwebview-how-to-display-links-to-http-pages

https://stackoverflow.com/questions/52258827/ios-wkwebview-doesnt-load-http-web-content

Swift OpenGL ES踩坑

最近在把一份iOS obj-c代码移植到swift,其中有OpenGL ES,swift的OpenGL资料是在太少,一步一个坑。记录一下

func glkView(_ view: GLKView, drawIn rect: CGRect)只调用一次问题

费尽心机移植完后,发现func glkView(_ view: GLKView, drawIn rect: CGRect)只在界面初始化的时候调用,而不是每帧都调用。最终在网络的小角落里找到了解决方法。在GLKViewController设置self.isPaused = false

绘制的图像存在拉抻问题,Y方向要长于X方向

这个问题其实在obj-c的代码里就存在了,当时只是根据屏幕大小来设置一下y方向大致的scale,但是没明白所以然,scale也是试出来的不是由算式算出来的。

经过在网上搜索,有幸看到了解决方法。设置projectionMatrix的aspect,aspect为OpenGL绘制区域的width/height。

VolumeModel.effect.transform.projectionMatrix = GLKMatrix4MakePerspective(Float(0.25*Double.pi), 2.0/3.0, 2, -1)

参考

https://www.jianshu.com/p/ed7fb9555839

swift OpenGL ES参考

https://www.jianshu.com/p/e0a7fe158d9c

系列文章

以及

https://github.com/skyfe79/LearningOpenGLES2

OpenGL绘制图像大小校准

由于程序中有用Quartz2D绘图,相当于在两个坐标系。而且OpenGL的摄像机的z轴高度是影响绘制的2D图像大小的因素。怎么把OpenGL绘制的图像与Quartz2D绘制的图像校准则是个问题。

前提条件:

1、OpenGL是以屏幕中心为(0,0),向上为y正向,向右为x正向。

2、OpenGL的画布大小由屏幕的Y方向大小决定

3、Quartz2D在add view时,以屏幕中心为y方向中点。画布高度由宽度决定。(高度是宽度的x倍)

4、Quartz2D绘制的图像大小是由屏幕宽度为决定因素

方法:

1、OpenGL Y方向scale校准,在上一段落已经说明了。

2、在某一型号手机上依然通过试验的方法,确定一个OpenGL的scale,还有OpenGL的Y方向偏移量,使得OpenGL的图像与Quartz2D图像吻合。

3、对这个scale、y方向偏移量,乘以系数,系数因子为屏幕宽/屏幕高

这样无论在何种机器上,都可以吻合,不会出现因为屏幕宽高不同而导致的错位问题

Spring Cloud学习笔记

参考:

https://blog.csdn.net/forezp/column/info/15197

https://cloud.tencent.com/developer/news/334738

https://www.cnblogs.com/ityouknow/p/7508306.html

服务的注册与发现Eureka
server

在main所在类使用@EnableEurekaServer注解,注册成为Eureka Server

配置参考:

server:
  port: 8761

eureka:
  server:
    renewalPercentThreshold: 0.49
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

spring:
  application:
    name: eurka-server

server:port:是自身端口,defaultZone的url是client的defaultZone要填的url

client
@EnableEurekaClient注册成为Eureka Client

配置参考:

server:
  port: 8762

spring:
  application:
    name: service-hi

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

分别启动,访问http://localhost:8761