首页 文章 精选 留言 我的

精选列表

搜索[优化],共10000篇文章
优秀的个人博客,低调大师

利用策略模式优化过多 if else 代码

前言 不出意外,这应该是年前最后一次分享,本次来一点实际开发中会用到的小技巧。 比如平时大家是否都会写类似这样的代码: if(a){ //dosomething }else if(b){ //doshomething }else if(c){ //doshomething } else{ ////doshomething } 条件少还好,一旦 else if 过多这里的逻辑将会比较混乱,并很容易出错。 比如这样: 摘自 cim 中的一个客户端命令的判断条件。 刚开始条件较少,也就没管那么多直接写的;现在功能多了导致每次新增一个 else 条件我都得仔细核对,生怕影响之前的逻辑。 这次终于忍无可忍就把他重构了,重构之后这里的结构如下: 最后直接变为两行代码,简洁了许多。 而之前所有的实现逻辑都单独抽取到其他实现类中。 这样每当我需要新增一个 else 逻辑,只需要新增一个类实现同一个接口便可完成。每个处理逻辑都互相独立互不干扰。 实现 按照目前的实现画了一个草图。 整体思路如下: 定义一个 InnerCommand 接口,其中有一个 process 函数交给具体的业务实现。 根据自己的业务,会有多个类实现 InnerCommand 接口;这些实现类都会注册到 Spring Bean 容器中供之后使用。 通过客户端输入命令,从 Spring Bean 容器中获取一个 InnerCommand 实例。 执行最终的 process 函数。 主要想实现的目的就是不在有多个判断条件,只需要根据当前客户端的状态动态的获取 InnerCommand 实例。 从源码上来看最主要的就是 InnerCommandContext 类,他会根据当前客户端命令动态获取 InnerCommand 实例。 第一步是获取所有的 InnerCommand 实例列表。 根据客户端输入的命令从第一步的实例列表中获取类类型。 根据类类型从 Spring 容器中获取具体实例对象。 因此首先第一步需要维护各个命令所对应的类类型。 所以在之前的枚举中就维护了命令和类类型的关系,只需要知道命令就能知道他的类类型。 这样才能满足只需要两行代码就能替换以前复杂的 if else,同时也能灵活扩展。 InnerCommand instance = innerCommandContext.getInstance(msg); instance.process(msg) ; 总结 当然还可以做的更灵活一些,比如都不需要显式的维护命令和类类型的对应关系。 只需要在应用启动时扫描所有实现了 InnerCommand 接口的类即可,在 cicada 中有类似实现,感兴趣的可以自行查看。 这样一些小技巧希望对你有所帮助。 以上所有源码可以在这里查看: https://github.com/crossoverJie/cim 你的点赞与分享是对我最大的支持

优秀的个人博客,低调大师

node上的redis调用优化示例

Node.js读写数据到influxDB,目前已经有一个库node-influx, 这个库功能非常强大,但是我个人使用这个库的时候,遇到无法解决的问题。 使用curl都可以写数据到influxDB,但是用node-influx总是报错,搞了半天也无法解决,就索性不用它了。 influxDB提供HTTP的API,也就是说Node.js中的axios或[request等HTTP客户端工具是可以直接和influx交互的。 需要注意的一点是,写到influxDB的数据格式必须是二进制流。 1. 字符串转二进制 const data = Buffer.from('mymeas,mytag=1 myfield=90') 2. 设置请求Content-Type为二进制 'Content-Type': 'application/octet-stream' 完整代码展示: \`const axios = require(\`\`'axios'``)` \`const data = Buffer.from(\`\`'mylog,name=wdd error\_count=2003,no\_send=0'``)` \`axios({\` \`url:\` `'\[http://localhost:8923/write?db=poc&rp=poc\](http://localhost:8923/write?db=poc&rp=poc)'``,` \`method:\` `'post'``,` \`headers: {\` `'Content-Type'``:` `'application/octet-stream'` `},` \`data: data\` `})` `.then((res) => {` \`console.log(\`\`'ok'``)` `// console.log(res)` `})`//欢迎加入全栈开发交流圈一起学习交流:864305860 `.\`\`catch\`\`((err) => {`//帮助突破技术瓶颈,提升思维能力 \`console.log(\`\`'err'``)` `})` 使用axios或者requst这种底层库的好处是,你用curl做的成功的任何操作,都可以转换成axios或request的请求,而不依赖与其他库。

优秀的个人博客,低调大师

Android代码优化之Handler内存泄漏

目录 目录 前言 Android代码中涉及进程间通信的地方经常会使用Handler。 public class MainActivity extends AppCompatActivity { //可能引入内存泄漏的方法 private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } 而上面这种用法可能会导致内存泄漏,比如使用如下代码,由于消息会在5分钟后发送,所以当用户进入并退出Activity后,在消息发送并处理完成之前,这个Activity是不会被回收的,这样就会导致内存泄漏。 public class MainActivity extends AppCompatActivity { //可能引入内存泄漏的方法 private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //5分钟后发送消息 handler.postDelayed(new Runnable() { @Override public void run() { Log.e("测试","123"); } },1000*10*5); } } 内存泄漏的原因 由于Handler、Looper和MessageQueue是一起工作的,在Android中一个应用启动后系统会默认创建一个用于处理主线程所有Message对象的Looper对象,它的生命周期贯穿于整个应用的生命周期,所以在主线程使用Handler的时候都会默认绑定这个Looper对象并关联MessageQueue,这时发送到MessageQueue的Message对象都会持有这个Handler对象的引用,这样Looper在处理消息的时候才能回调到Handler中年的handleMessage方法。因此如果Message对象还没有处理完成,那么Handler对象也就不会被垃圾回收。而在Java中非静态内部类会持有外部类的一个隐式引用,所以当Handler无法回收时由于Handler持有MainActivity的引用导致MainActivity也无法被回收,从而导致内存泄漏。 解决方法:将Handler改为静态内部类并使用弱引用 public class MainActivity extends AppCompatActivity { private final InnerHandler mInnerHandler=new InnerHandler(this); private final Runnable sRunnable=new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "解决方法", Toast.LENGTH_SHORT).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mInnerHandler.postDelayed(sRunnable,1000*60*5); } private static class InnerHandler extends Handler{ private final WeakReference<MainActivity> activityWeakReference; public InnerHandler(MainActivity activity) { this.activityWeakReference = new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity activity=activityWeakReference.get(); if(activity!=null){ } } } } 个人技术博客:https://myml666.github.io/ 我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。

优秀的个人博客,低调大师

Spark性能优化:基于分区进行操作

版权声明:本文由董可伦首发于https://dongkelun.com,非商业转载请注明作者及原创出处。商业转载请联系作者本人。 https://blog.csdn.net/dkl12/article/details/82496977 我的原创地址:https://dongkelun.com/2018/09/02/sparkMapPartitions/ 前言(摘自Spark快速大数据分析) 基于分区对数据进行操作可以让我们避免为每个数据元素进行重复的配置工作。诸如打开数据库连接或创建随机数生成器等操作,都是我们应当尽量避免为每个元素都配置一次的工作。Spark 提供基于分区的map 和foreach,让你的部分代码只对RDD 的每个分区运行一次,这样可以帮助降低这些操作的代价。 当基于分区操作RDD 时,Spark 会为函数提供该分区中的元素的迭代器。返回值方面,也返回一个迭代器。除mapPartitions() 外,Spark 还有一些别的基于分区的操作符,见下表: 函数名 调用所提供的 返回的 对于RDD[T]的函数签名 mapPartitions() 该分区中元素的迭代器 返回的元素的迭代器 f: (Iterator[T]) → Iterator[U] mapPartitionsWithIndex() 分区序号,以及每个分区中的元素的迭代器 返回的元素的迭代器 f: (Int, Iterator[T]) → Iterator[U] foreachPartitions() 元素迭代器 无 f: (Iterator[T]) → Unit 首先给出上面三个算子的具体代码示例。 1、mapPartitions 与map类似,不同点是map是对RDD的里的每一个元素进行操作,而mapPartitions是对每一个分区的数据(迭代器)进行操作,具体可以看上面的表格。 下面同时用map和mapPartitions实现WordCount,看一下mapPartitions的用法以及与map的区别 package com.dkl.leanring.spark.test import org.apache.spark.sql.SparkSession object WordCount { def main(args: Array[String]): Unit = { val spark = SparkSession.builder().master("local").appName("WordCount").getOrCreate() val sc = spark.sparkContext val input = sc.parallelize(Seq("Spark Hive Kafka", "Hadoop Kafka Hive Hbase", "Java Scala Spark")) val words = input.flatMap(line => line.split(" ")) val counts = words.map(word => (word, 1)).reduceByKey { (x, y) => x + y } println(counts.collect().mkString(",")) val counts1 = words.mapPartitions(it => it.map(word => (word, 1))).reduceByKey { (x, y) => x + y } println(counts1.collect().mkString(",")) spark.stop() } } 2、mapPartitionsWithIndex 和mapPartitions一样,只是多了一个分区的序号,下面的代码实现了将Rdd的元素数字n变为(分区序号,n*n) val rdd = sc.parallelize(1 to 10, 5) val res = rdd.mapPartitionsWithIndex((index, it) => { it.map(n => (index, n * n)) }) println(res.collect().mkString(" ")) 3、foreachPartitions foreachPartitions和foreach类似,不同点也是foreachPartitions基于分区进行操作的 rdd.foreachPartition(it => it.foreach(println)) 4、关于如何避免重复配置 下面以打开数据库连接举例,需求是这样的: 读取mysql表里的数据,做了一系列数据处理得到结果之后,需要修改我们mysql表里的每一条数据的状态,代表程序已经处理过了,下次不需要处理了。 4.1 表 以最简单表结构示例 字段名 注释 ID 主键、唯一标识 ISDEAL 程序是否处理过 建表语句 CREATE TABLE test ( id INTEGER NOT NULL AUTO_INCREMENT, isdeal INTEGER DEFAULT 0 NOT NULL, primary key(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; 4.2 不基于分区操作 一共用两种方法 4.2.1 第一种 package com.dkl.leanring.spark.sql.mysql import org.apache.spark.sql.SparkSession object UpdateMysqlDemo { def main(args: Array[String]): Unit = { val spark = SparkSession.builder().appName("UpdateMysqlDemo").master("local").getOrCreate() val database_url = "jdbc:mysql://192.168.44.128:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false" val user = "root" val password = "Root-123456" val df = spark.read .format("jdbc") .option("url", database_url) .option("dbtable", "(select * from test where isDeal=0 limit 5)a") .option("user", user) .option("password", password) .option("driver", "com.mysql.jdbc.Driver") .option("numPartitions", "5") .option("partitionColumn", "ID") .option("lowerBound", "1") .option("upperBound", "10") .load() import java.sql.{ Connection, DriverManager, ResultSet }; df.rdd.foreach(row => { val conn = DriverManager.getConnection(database_url, user, password) try { // Configure to be Read Only val statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) val prep = conn.prepareStatement(s"update test set isDeal=1 where id=?") val id = row.getAs[Int]("id") prep.setInt(1, id) prep.executeUpdate } catch { case e: Exception => e.printStackTrace } finally { conn.close() } }) spark.stop() } } 上面的代码,取isDeal=0的前五条,因为造的数据量少,所以只取了前五条,然后指定了五个分区,这里只是一个代码示例,实际工作中应该数据量很大,每个分区肯定不止一条数据 根据上面的代码,看到用这种方式的缺点是每一个元素都要创建一个数据库连接,这样频繁创建连接、关闭连接,在数据量很大的情况下,势必会对性能产生影响,但是优点是不用担心内存不够。 4.2.2 第二种 val conn = DriverManager.getConnection(database_url, user, password) try { val statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) val prep = conn.prepareStatement(s"update test set isDeal=1 where id=?") df.select("id").collect().foreach(row => { val id = row.getAs[Int]("id") prep.setInt(1, id) prep.executeUpdate }) } catch { case e: Exception => e.printStackTrace } 这种方式的缺点是把要操作的数据全部转成scala数组,仅在Driver端执行,但是如果数据量很大的话,可能因为Driver内存不够大而抛出异常,优点是只建立一次数据库连接,在数据量不是特别大,且确定Driver的内存足够的时候,可以采取这种方式。 4.3 基于分区的方式 df.rdd.foreachPartition(it => { val conn = DriverManager.getConnection(database_url, user, password) try { val statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) val prep = conn.prepareStatement(s"update test set isDeal=1 where id=?") it.foreach(row => { val id = row.getAs[Int]("id") prep.setInt(1, id) prep.executeUpdate }) } catch { case e: Exception => e.printStackTrace } finally { conn.close() } }) 这种方式就结合了上面两种方式的优点,基于分区的方式使得创建连接的次数不会那么多,然后每个分区的数据也可以平均分到每个节点的executor上,避免了内存不足产生的异常,当然前提是要合理的分配分区数,既不能让分区数太多,也不能让每个分区的数据太多,还有要注意数据倾斜的问题,因为当数据倾斜造成某个分区数据量太大同样造成OOM(内存溢出)。 4.4 其他 上面只是列举了一个例子,且只是在foreach这样的action算子里体现的,当然肯定也有需求需是在transformation里进行如数据库的连接这样的操作,大家可类比的使用mapPartitions即可 5、其他优点(未证实) 网上有很多博客提到mapPartitions还有其他优点,就是mapPartitions比map快,性能高,原因是因为map的function会执行rdd.count次,而mapPartitions的function则执行rdd.numPartitions次。 但我并这么认为,因mapPartitions的function和map的function是不一样的,mapPartitions里的迭代器的每个元素还是都要执行一遍的,实际上也是执行rdd.count次。 下面以其中一篇博客举例(只列出优点,大部分博客上的写的都一样的,应该出自同一篇博客吧~) 博客地址:Spark—算子调优之MapPartitions提升Map类操作性能 至于mapPartitions是否真的比map处理速度快,如果我有时间验证得到结果的话,我再更新一下这个地方~ 相关阅读 Spark Sql 连接mysql Spark 创建RDD、DataFrame各种情况的默认分区数

优秀的个人博客,低调大师

POI框架EXCEL解析性能优化

背景 在做商品EXCEL的时候,线上发现了Full GC,排查得知是商家搞了一个巨大的excel,单商品发布接口平均耗时400ms(调用sell耗时200ms左右,系统自身处理商品同步耗时150ms左右),对于3000个商品的发布,耗时在20min左右,这20min内该excel的内存一直未能释放。 第一时间想到的是POI真坑,真吃内存。 事情发生了就想着怎么处理, 止血 线上机器分批重启, 马上加一个excel行数的限制然后发布 线上半个小时左右就没有任何问题了。 思考 为什么poi这么吃内存,poi这么老了,肯定有人踩过这个坑,撸起袖子,搜poi full gc. 很多文档将的都太粗糙了,本质没有说透 原因 excel本质上是xml文件的集合体。从office 2007起开始使用xml来存档和数据交换:https://zh.wikipedia

优秀的个人博客,低调大师

简单优化前端工程几种方式(上篇)

使用CDN 如果A网站和B网站, 同时使用了 https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js的资源, 在访问其他网站A时, 浏览器会缓存jquery.min.js资源文件, 访问网站B时,就可以免于下载jquery.min.js的文件, 这样既节省了服务器流量, 又提升了资源加载的速度, 一举两得~ 减少网络请求 使用雪碧图(用Photoshop将多个icon拼接到一张图上) 将多个js文件合并压缩为单个js文件(使用Webpack,或在线工具) 减少图片尺寸 可以使用PP鸭一键压缩, 具体介绍: PP鸭为你的图片瘦瘦瘦身 (支持快速压缩jpg, png, gif三种格式) 将图片保存为渐进式jpg 将图片保存为渐进式jpg后, 图片在浏览器显示时, 可以由模糊到清晰渐进式加载, 提升用户体验 将javascript脚本放到html的底部 HTML文件是从上往下渲染的, 如果js脚本有大量耗时操作, js执行期间, 用户是不能看到页面的 mdn的解释链接: https://developer.mozilla.org/zh-CN/docs/Learn/Getting_started_with_the_web/JavaScript_basics 一般我们会把css样式文件放在html的头部, javascript脚本文件放在页面底部, 但什么事情都有特例, 比如bootstrap的部分组件依赖jquery,也就是jquery必须在bootstrap之前引入, 如果我们要在页面头部引入bootstrp样式的话, 那jquery也必须放到了页面的头部!

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册