🏠

晓东 - 持续改进

综合

整体

综述
  • 核心:SEO+ASO改造、服务器成本控制、App按需求演进、广告联盟优化。
  • 更多:最常用或反直觉技术操作、GAE免费资源利用、云平台免费产品探索。
实战(冗余不分类)
  • 发文章、维护专题;网页加或改description、规范化URL。
  • 更多:上架App改善;游戏策划改造。
常识
生活
  • 信用卡年费规则:
      免年费条件 - 每年使用信用卡消费满5次或12次,比如在超市或网上购物,但不包括取现。
  • ...
赚钱
Web和App广告
  • 广告联盟:
      AdSense - 赚取网站广告费。
      AdMob - 赚取手机应用广告费。
      Google Ad Manager - 赚取AdSense、AdMob和第三方广告联盟及直销商的广告费;必须拥有AdSense账号(必须关联吗?)。
      Ads - 花钱给谷歌打广告。
    
      百度联盟 - 赚取网站广告费。
  • ...

技术

原则
  • 开发环境 Windows防火墙安全设置
    
    服务器间网络通讯应经常检查,避免丢包、ping值高或无法连接等情况。
      注意 - 通常去程丢包率跟回程差不多,但部分服务器(如Vultr东京)则还应加测回程丢包情况,严重影响nginx反向代理大陆服务器。
最常用或反直觉
  • 综合
    浏览器超链接文字部分选中 - 按住Alt键不松并拖动鼠标。
    远程桌面顶侧操作条支持缩小移动,可通过拖拽来避免阻挡程序窗口标题。
    Servlet:
      quarkus-undertow-3.13.2 最高支持 Servlet 4.0 (Undertow路线图);而 Open Liberty 则最高支持 Servlet 6.0
SEO
编程指导
  • Java:
      快速启动可采用基于CRIU的Open Liberty InstantOn,原理是构建镜像时将内存中的Java程序直接快照化。
      镜像 FROM open-liberty:beta-java17 认为JDK17,而 FROM icr.io/appcafe/open-liberty:beta 则默认为JDK21。
      优化内存占用可采用基于GraalVM的Quarkus,原理是编译时会转译为本地原生程序。
  • Java相关:
      解决 - Java 8 date/time type `java.time.OffsetDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
        // implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2")
        ObjectMapper om = JsonMapper.builder().addModule(new JavaTimeModule()).build();
  • Java Vs. C#:
      类型转换 - 均支持(NewObj)OldObj;候选转换(仅单层括号):Java用NewObj.class.cast(OldObj).x(); C#用(OldObj as NewObj).x();
      方法覆盖和隐藏 - Java子类存在父类同名方法即覆盖;执行实际对象的方法;而C#同名(或new修饰的方法)则是隐藏方法,即根据声明类型而决定执行谁的方法,且覆盖方法必须存在关键词 父virtual+子override 。
  • Gradle:
      为了方便统一构建,可用上gradle默认任务(./gradle时无需任务名,可写至build.gradle首行) - defaultTasks("clean","run");
  • Godot:
      必须在【编辑器设置→导出→Android】中指定 Java17 版本(版本不能高也不能低) java_sdk_path = E:/java/jdk-17_windows-x64_bin/jdk-17.0.12 和 android_sdk_path = C:\Users\person\AppData\Local\Android\Sdk
      shader_cache超长路径报 Safe save failed,临时解决:编辑器设置->文件系统->保存时->勾掉 Safe Save...。
      定位玩家应首选Godot场景唯一节点: GetNode<Node3D>("%Player") // Player节点右键点"% Access as Unique Name"
      工作流:
        资源导入导出 - 总体原则是实现直接导入,但不进行额外设置,且也不能破坏在用属性值,解决办法是通过动画树隔离下:
          AnimationTree.TreeRoot(AnimationNodeBlendTree等).AddNode("动画名", AnimationNodeAnimation(必须UseCustomTimeline=true时属性LoopMode方生效)) 。
技术点
  • async/await
    说明:
      作用 - 阻塞当前方法块儿至完成状态,但不阻塞方法调用处,利用一轮轮分配的CPU时间片进行计算。
      async方法并不阻塞当前线程,可通过 ConcurrentHashMap<String, Semaphore> 来防止重入。
生产环境
  • 观察
    GAE曾被1秒数次访问的抓取工具冲垮: 故应判断userAgent为"Go-http-client/1.1"时跳过请求。
场景解决方案
  • 资源
    说明:
      非UGC图片,可直接放页面同级子目录(images)内;UGC图片或视频应传至云端存储(GCS、OSS、COS等)。
    分页
    说明:
      Java可选用 https://mybatis.io/ ; 或 Jakarta Data 的 Page<Model> 。
    限流、幂等、防盗刷
    ...
    上传
    说明:
      按大陆宽带上行速率30Mbps(3.75MB/s)算,50MiB文件及以下大小上传时等待业务做完再响应,GAE取30MiB;大于50MiB至150MiB则上传后返回文件网址,接着再传入新请求做业务;大于150MiB的则分块儿上传。
      按nginx默认1分钟响应超时算,亲测100MiB上传至GCS用时46秒,中转时会有损耗,故50MiB至100MiB就要返回网址。
      GCS“可续传上传”仅指按序断点续传,并不是各分片同时无序上传的“并行复合上传”。
    
    文件选取:document.getElementById("file").files[0]或window.showOpenFilePicker()
    
    上传大小限制及响应超时:
      避免上传中断的重传耗时 - 分片上传+断点续传
        数据块大小至少8MiB起;可续传有效期为7天。
        前端分片: document.getElementById('fileInput').files[0].slice(start, start + chunkSize);
        后端合并: FileChannel.open(path).write(bb); // 性能超RandomAccessFile
    
        GCS可续传上传的做法分为: 
          单块 - 根据上次停止位置来接续。
          多块 - 根据数据块字节范围来同时上传;亲测若分片同时上传报503,依序上传则正常响应308;
            最后一块儿会响应 200 + MD5值(Etag: "e92c1cff62ac6e5662eecb292c572e06")
    
          注意 - fetch上传至SessionURI时会自动添加Origin头,但后端HttpClient则要手动添加。
          猜测:各数据块将总字节写满即判定为可续传状态为“完成”。
          参考:https://gcp.infoq.cn/details/1305.html
          Java客户端:SignUrlOption.withExtHeaders(ImmutableMap.of("x-goog-resumable", "start"));
            通过返回的signUrl换取(POST/允许CORS头x-goog-resumable)含upload_id参数的Location上传网址。
    
    服务端执行进度回报至客户端或浏览器:
      JAX-RS SSE - 长业务或大文件上传进度场景。
        @Produces(MediaType.SERVER_SENT_EVENTS) - https://openliberty.io/guides/reactive-messaging-sse.html#creating-the-sse-api-endpoint
                                            
其他
NetBeans
  • NetBeans插件能识别的属性:
      -PrunEnvironment=设置(=赋值)或移除(!前缀)空格分割的环境变量 -PrunJvmArgs=... -PrunWorkingDir=...
    
      解决NetBeans插件io.quarkus将环境变量拼接为java -jar之-D参数时报IOException: invalid null character in command(非NetBeans环境则无需该操作):
          quarkusRun -PrunEnvironment='!CUDA_PATH_V12_3 !EFC_4908'
  • ...