程序性能优化入门锦集--设计+代码+JVM调优+数据库优化策略

关于优化是一项很大的内容。本文结合菜鸟结算项目优化点分析以及书籍《JAVA程序性能优化》阅读心得,给出个人觉得可供参考的优化思路,共涉及四个方面,分别是:设计篇、代码优化篇、JVM内存调优和数据库操作优化。若文中理解有误之处也欢迎底下评论指正。

所谓优化的目的不止是使得我们的程序更快,也使得我们遇到峰值情况应用会更加稳定可靠,程序优化在某个程度上也可以理解是功能保障的升级篇。优化可能只需要在几个很微小的地方做些许改动,但是优化可能是无穷的。那么不得不提优化点重点放在哪里?根据木桶原理我们知道系统中最终性能取决于系统中性能表现最差的组件,根据应用的特点不同,任何计算机资源都有可能成为系统瓶颈,其中最有可能成为系统瓶颈的计算资源如下:

  • 磁盘IO:磁盘读写要比IO慢的多,低效的IO影响无疑是巨大的,个人优化想法是通过设计篇中“缓冲”部分来进行优化。
  • 异常、锁竞争、数据结构变动:高频的异常捕获处理和激烈的锁竞争,会占用宝贵的CPU资源。优化可以参考代码优化篇。
  • 高频JVM内存操作:我们的应用部署前很多是经历了JVM参数调整的,给应用指定了占用内存大小等,所以应用需要关注更多的是JVM内存堆、栈情况而不是系统的内存情况。调整JVM内存参数也是有一定讲究的,可以参考JVM内存调优篇。
  • 数据库优化:对于非计算密集型应用,数据库可能会成为系统的瓶颈。相对而言数据库优化起来是较为困难的,但若优化得当系统效率会得到很大的提升,该部分有想法可以参考下数据库优化篇。

设计篇

设计优化是最上层的优化,往往在应用开发之初针对模块的潜在问题给出的设计方案,涉及到系统的实现,它的优化是不容忽视的。该篇针对两点:缓存和缓冲,做一些文章。

缓冲(Buffer)

缓冲的一个典型比喻是漏斗,上层系统如茶壶,下层系统如水瓶。出水速度很快,水瓶瓶口很细,水倒入水瓶相当于内存数据写入硬盘,内存输入快,硬盘写入慢,应用程序上下层之间存在性能差异。那么缓冲的作用就在于协调上层、下层组件间的性能差异,当上层组件性能优于下层组件时,可以有效减少上层组件对下层的等待时间。基于缓冲,上层组件不需要等待下层组件真实的接受全部数据,就可以返回操作,加快了自身的处理速度。
缓冲
缓冲最常用的场景就是提高I/O速度,它的实现本质是一块特定的内存区域。闲话不多说,上代码:

@Test
public void test_IO_case() throws Exception {//无缓冲//Writer writer = new FileWriter(new File("file.txt"));//有缓冲Writer writer = new BufferedWriter(new FileWriter(new File("file.txt")));       Long begin = System.currentTimeMillis();for (int i = 0; i < 100000; ++i) {writer.write(i);}writer.close();System.out.println("testFileWriterBuffer spend: " + (System.currentTimeMillis()-begin));
}

上述无缓冲和有缓冲的情况,性能差距大约是2倍,前者63ms,后者32ms。我的本机跑前者是85ms,后者是13ms,提升巨大。
JDK常见I/O组件封装有BufferedWriter、BufferedOutputStream,JDK1.4后还提供了更强大的缓冲组件NIO,其基于selector异步网络、提供文件访问接口、新增了Channel通道抽象进行双向读写等特性,有兴趣可以了解一下。需要注意的是,一般来说缓冲区不宜过小,否则无法起到真正的缓冲功能,也不宜过大,否则会增加内存GC负担(GC看JVM调优篇)。BufferedWriter、BufferedOutputStream都可以加第二个参数指定缓冲大小,默认不填则大小为8K。

缓存(Cache)-不得不提的Tair

缓存的主要作用是暂存数据处理结果,并提供下次访问使用。这个特性针对高频调用场景是极有效果的,可以直接从缓存中快速获取高频数据,减少不必要的数据库读写操作,使用时非常普遍的,所以不得不提到集团中的中间件--Tair。
有三种Tair的产品分别是MDB、RDB和LDB。其中MDB属于内存型产品,支持KV和类hashmap结构,性能最优,但不支持持久化存储;RDB支持List,Set,Zset等复杂的数据结构,性能次之,可提供缓存和持久化存储两种模式;LDB属于持久化产品,支持KV和类hashmap结构,性能较前两者最差,但持久化可靠性最高。MDB单机30WQPS,99%请求在1ms之内完成,可见性能之高。Tair百科看这里:中间件文档中心Tair,此外:Tair优化实践
缓存的Tair使人欢喜使人愁。欢喜的是太好用的,运用效果显著;愁的是使用如果不得当也会引发意想不到的问题,而这些问题往往起初是被人忽视的,当展现出来的时候往往引发了不堪设想的后果。这里重点讲一下在项目中曾经发现的问题--Tair限流策略和热点Key解决方案。

Tair限流策略

我们看一下申请Tair实例时:
申请Tair
这里是有一个规格选择的。由于大部分业务方均使用公共大集群,Tair根据业务方申请时提供的QPS峰值,来评估整体集群能力是否可以满足业务方需求。所以申请Tair空间即表示接受当用户使用超出申请资源时,Tair有权对访问进行限流。具体限流策略看这里:Tair限流策略。这里就引发了一个问题,当热点Key突发时(有的甚至存储的是Pkey-Skey结构,举个例子某条数据如下结构:群id:{成员id:成员信息value},群id和成员id即是Pkey和Skey),问题如何排查才能使Tair不被击穿?

热点Key解决方案

上代码看一看:

public class DragonhorseTairWrapper {private MultiClusterTairManager dragonhorseTairManager;public void init(){if (dragonhorseTairManager == null){throw new RuntimeException("tair init Error!");}else{// 该行开启客户端原生 Localcache// namespace,最多缓存10000个key,本地缓存过期时间500msdragonhorseTairManager.setupLocalCache(DragonhorseTairConstant.NAMESPACE, 10000, 500);// 开启热点key(Hot-running)工作模式dragonhorseTairManager.enableLocalCacheImprove(DragonhorseTairConstant.NAMESPACE);}}public void setDragonhorseTairManager(MultiClusterTairManager dragonhorseTairManager) {this.dragonhorseTairManager = dragonhorseTairManager;}
}

热点key原理是:Tair 服务端版本会在运行时统计维护当前的热点key状态,当客户端访问到热点key时,热点通知的feedback包会随着客户端的get类的请求一并返回。客户端对热点key的识别依据服务端热点反馈的feedback包。
收到服务端反馈的热点key后,客户端依赖自身的的Localcache功能,每次写操作会自动强制删除Localcache里存在的key,读操作后会自动从Localcache里读取,Localcache中不存在则从服务端获取,成功后存储到Localcache 里。在热点key防御系统中,客户端要开启hot-running 模式,该模式下只能缓存带热点标记的key,Localcache 中非热点的key将逐步被淘汰。即一旦开启客户端的该模式,会强制改变Localcache的工作模式。
具体热点key解决方案及数据对比看这里,非常详细:还因突发热点击穿DB产生故障?赶紧看这里

代码优化篇

代码优化体现在程序的方方面面了,可能是算法、可能是数据结构,但是我们也不能小看它。也许极其微小的结构变动,所带来的优化效果也是不一般的,具体可以看《java程序优化》一书,非常详细,下面列举几个我整理的大家可能使用到的地方,一千万的循环来放大对比,给出前后优化耗时:

  1. 慎用try-catch,尤其是在循环之中,可以的话将其移到循环之外。循环内110ms,循环外62ms
  2. 多用局部变量。由于局部变量在栈中,其它如静态变量的多在堆中,相比之下变量在栈中会更优秀一些。使用static变量266ms,使用局部变量78ms。
    3.位运算代替乘、除法。原219ms,位运算后31ms。

4.switch在循环中可以用数组代替(书中理论)。比较有意思的是我的本机上跑循环中switch耗时8ms,用map/array替代耗时32ms。上代码如下,这个点需要斟酌一下,欢迎各位尝试:

@Test
public void test_测试循环中switch和数组耗时对比() throws Exception {int re = 0;Stopwatch stopwatch = Stopwatch.createStarted();//循环一千万次放大耗时对比//单纯swicthfor (int i = 0; i < 10000000; ++i) {re = switchInt(i);}System.out.println("循环中switch耗时(ms): " + stopwatch.elapsed(TimeUnit.MILLISECONDS));//使用数组int [] sw = new int[]{3, 6, 7, 8, 10, 16, 18, 999999999,-1};Stopwatch stopwatch_array = Stopwatch.createStarted();for (int i = 0; i < 10000000; ++i) {re = arrayInt(sw, i);}System.out.println("用map/array替代耗时(ms): " + stopwatch_array.elapsed(TimeUnit.MILLISECONDS));
}
protected int switchInt (int index) {int i = index % 9;switch (i) {case 0 : return 3; case 1 : return 6;case 2 : return 7; case 3 : return 8;case 4 : return 10; case 5 : return 16;case 6 : return 18; case 7 : return 999999999;default : return -1;}
}
protected int arrayInt (int[] sw, int index) {int i = index % 9;return sw[i];
}

5.提取公共表达式。提取前156ms,提取后78ms。
6.展开循环,拉开迭代器增长步长。有必要说一下,这会导致可读性变差,也是没有办法的办法才这么搞。优化前94ms,优化后31ms。
7.静态static方法代替实例方法。

并发

这里需要着重说明一下并发过程的优化,这里也是有优化策略的。首先由于数据同步,并发结构需要更改如下:

List : Collections.synchronizedList(List)

Set : Collections.synchronizedList(Set)
HashMap : concurrentHashMap
Queue : concurrentLinkedQueue
volatile : 变量其它线程可见
synchronized : 锁方法,内部锁
ReadWriteLock : 重入锁
ThreadLocal : 局部变量,每个变量一个副本
semaphore :信号量,指定多个线程同时访问某一资源

关于锁,有以下几种方法:

  1. 减小锁的范围,举个例子,如果某条记录操作数据库,如果可以将这条记录锁住,那就千万不要用锁整个数据库的方式,而使用将这条记录锁住。
    2.减小锁粒度。如concurrentHaspMap结构,其内部是分为16段的,减少Hash时冲突问题。当然这也存在一个问题,就是concurrentHaspMap作为全局使用时,需要获取全部的16个锁。

3.锁分离。LinkedBlockingQueue,基于链表前端、尾端同时操作;此外读写分离锁(针对读多写少的情况)也是这个思想。
4.锁粗化:循环内频繁使用锁时,放在循环之外。
这个地方有时间会继续补充,添上些代码范例。

JVM调优篇

在看JVM调优前,需要了解一些JVM内存模型,可以看下面这站图片:
JVM内存模型
这其中,Java堆存储运行对象如数组,方法区存类元数据结构如常亮,程序计数器存储指令,虚拟机栈保存函数调用堆栈信息,本地方法栈保存java函数调用(线程私有),本地方法栈管理本地方法调用,后两者都会抛出StackOverFlowError和OutOfMemoryError。

垃圾回收(Garbage Collection)

Java和C最大的不同有一点就是垃圾回收,C每次malloc或new后需要free,Java可以自主进行不用的数据变量的回收。深入Java堆,又可以细分,且看下图,:
java堆
有几个区是需要清楚的,新生对象保存在Eden伊甸区中,当伊甸区存储达到阈值,会触发一次Minor GC,可以理解为YoungGC,这些数据变量会经历一次垃圾回收,未被回收的会移至幸存区S0或S1。幸存区每次发生Minor GC也会被扫描一次进行垃圾回收,如此当幸存区满了或里面的数据变量超过15次没被回收完成时,变量会移入到老年区。老年区有一个阈值,当量达到老年区阈值时,会触发一次MajorGC,可以理解为FullGC。
本身FullGC如果不频繁出现并且时间短暂是可以接受的,但是当发生频繁并且严重的影响到了响应时间,这时就需要寻找代码问题进行优化了。若代码没有问题时,我们可以通过改变JVM参数来调整比例观察,这就到了我们JVM调优的内容。
关于JVM调优基础可以看这里:JVM调优基础
JVM调优
可以看看这篇文章,对内存模型有一个更深入的了解:java(JVM)内存模型和垃圾回收监控与调优(译)
FullGC怎么查?看这里:Full GC怎么查?-绝知此事要躬行

数据库优化策略

有兴趣的同学可以先看着两篇文章:

  • update+select两条SQL合并成单条SQL
  • AliSQL秒杀场景测试报告样例

且看藏金阁计量模块数据库优化实践:

/*** 更新计量增量到数据库记录中。* @param measure*         计量对象* @return 计量增加前的累计值快照** @implNote 1.如果为了极致的性能,可以考虑将这条sql和后续插入log表的sql合并写一个存储过程,减少网络消耗,也减少锁等待的时间。性能会有至少80%的提升。<br/>* 2.这里之所以不能将insert log提前到update之前,是因为log不仅需要确保幂等,还需要满足幂等后计量累计量快照的查询, 而且数据需要强一致性(不能延时和错误)。所以也就无法获得insert提前带来的锁时间减少的好处。*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)@Overridepublic BigDecimal persistIncrement(Measure measure) {Stopwatch stopwatch = Stopwatch.createStarted();String requestId = measure.getBizRequestId();long sysRequestId = measure.getSysRequestId();MeasureLog log = new MeasureLog();log.setIncrement(measure.getIncrement());log.setMeasureId(measure.getId());log.setRequestId(requestId);// 查询和更新计量累计值(这里为了DB性能考虑,使用了TDDL的hint功能,比较恶心的在业务代码中处理了分库的逻辑,如果能从TDDL中获取分库信息就比较优雅了)BigDecimal originalAggregate = measureDao.queryAndUpdateMeasureAggregateById(measure.getId(), measure.getIncrement(), measure.getId() % 4);// 线上不应该出现,但在开发环境中容易发生的错误AssertUtil.notNull(originalAggregate, () -> "计量累计值不应该为空,数据库有脏数据。可能导致的原因是手工删除了计量实例数据,但是未清除索引缓存。[sysRequestId=" + sysRequestId + "][requestId=" + requestId + "][measureId=" + measure.getId() + "][measureKey=" + measure.getMeasureKey() + "]", logger);originalAggregate = originalAggregate.subtract(measure.getIncrement());// 插入日志log.setAggregate(originalAggregate);Map<String, Object> params = new HashMap<>();params.put("requestId", log.getRequestId());params.put("measureId", log.getMeasureId());params.put("increment", log.getIncrement());params.put("aggregate", log.getAggregate());params.put("dbNum", log.getMeasureId() % 4);// tddl使用的groovy解析分库分表的表达式,所以hash是原生的Java String.hashCode()方法params.put("tableNum", String.format("%04d", Math.abs(log.getRequestId().hashCode()) % 32));// 这里可会会因为幂等约束抛出异常measureLogDao.insertWithHint(params);if (logger.isDebugEnabled()) {logger.debug("更新计量的增量到DB成功。[sysRequestId=" + sysRequestId + "][requestId=" + requestId + "][measure=" + measure + "][log=" + log + "][time=" + stopwatch.stop().elapsed(TimeUnit.MILLISECONDS) + "ms]");}// 都顺利完成,则返回原始计量累计值,如果中途发生错误则回滚事务。return originalAggregate;}
@Select("/*+TDDL({'type':'direct','vtab':'cf_measure','dbid':'CAINIAO_CF_CHARGE_000#{2}_GROUP','realtabs':['cf_measure_000#{2}']})*/ SELECT aggregate FROM UPDATE QUEUE_ON_PK #{0} `cf_measure` set aggregate = aggregate + #{1} where id = #{0}")BigDecimal queryAndUpdateMeasureAggregateById(long id, BigDecimal increment, long dbNum);@Insert("/*+TDDL({'type':'direct','vtab':'cf_measure_log','dbid':'CAINIAO_CF_CHARGE_000#{dbNum}_GROUP','realtabs':['cf_measure_log_${tableNum}']})*/ INSERT COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL INTO cf_measure_log (gmt_create ,gmt_modified ,request_id ,measure_id ,increment ,aggregate )VALUES(now(),now(),#{requestId},#{measureId},#{increment},#{aggregate})")void insertWithHint(Map<String, Object> params);

函数persistIncrement是为了更新计量增量到数据库记录中,改代码有两处是可值得我们借鉴和思考的地方,通过SQL合并和Hint优化,优化程度分别为89.98%和58.82%,系统最坏情况压测结果TPS从177.61提升至340,接着再提升至540:

  1. 使用TDDL Hint干扰执行路径。在默认条件下,SQL的执行先经过TDDL再走到最后的库操作,在我们清楚的范围之内,TDDL其实也提供给我们配置的操作,这就是Hint,它是一种“暗示”,告诉TDDL怎么走最优,从而对处理路径进行我们认知方向的修改,加速了中间件过程处理。
  2. 使用SQL合并。将update+select两条SQL合并成单条SQL,向客户端直接返回update之后的结果列,通过减小网络开销及sql解析代价提高性能。语法描述如下:
SELECTselect_expr [, select_expr ...]
FROM UPDATE[LOW_PRIORITY] [IGNORE][COMMIT_ON_SUCCESS] [ROLLBACK_ON_FAIL] [QUEUE_ON_PK] [TARGET_AFFECT_ROW num]
tbl_name
SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...  [WHERE where_condition]  [ORDER BY ...]  [LIMIT row_count]

说明:1)仅支持单个表UPDATE语句
2)SELECT返回的数据量为matched rows,而非modified rows,即逻辑更新而非物理更新,这个与PostgreSQl/Oracle是一致的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/69514.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴藏经阁,快来学习起来

阿里巴巴藏经阁 其中约有七千多本作品供大家免费下载&#xff0c;在线阅读&#xff01; 地址 https://developer.aliyun.com/ebook/?spma2c6h.26392459.ebook-detail.2.5ea12039WsHOsc

[导入]文章藏金阁

在网上用www.gougou.com订阅了很多RSS,但RSS保存功能又非常弱,我保存下来的文章全部统统放到一个目录下去了,时间长了,里面乱七八糟的.删除了吧...突然哪天想起原来看过一篇文章,却寻觅不着.用365key吧,我又经常用firefox.郁闷了郁闷了...先暂时自己建个文章藏金阁吧~~ 数据…

知乎高赞:java初学者如何通过自学快速找到第一份工作?

大家好&#xff0c;我是威哥&#xff0c;《RocketMQ技术内幕》一书作者&#xff0c;荣获RocketMQ官方社区优秀布道师、CSDN2020博客执之星Top2等荣誉称号。目前担任中通快递技术平台部资深架构师&#xff0c;主要负责全链路压测、消息中间件、数据同步等产品的研发与落地&#…

源码分析RocketMQ与运维实战

RocketMQ是笔者当前最突出的亮点&#xff0c;正是由于在CSDN中连载RocketMQ&#xff0c;最终促成了《RocketMQ技术内幕》一书的出版&#xff0c;也凭借此专栏的高质量&#xff0c;最终成为CSDN2020年年度博客之星TOP2。 RocketMQ专栏目前已经输出48篇文章&#xff0c;并且被阿…

知乎高赞:如果一个程序员工作5年后还没成为大牛,是不是该考虑别的路子了?

我觉得这个问题非常适合我&#xff0c;我的职业生涯前5年即没有大厂背景&#xff0c;也无好的学历背书&#xff0c;但后面痛定思痛寻找了一条通过技术分享实现逆袭的道路&#xff0c;而且我的成功的经验可以复制。 职业生涯的前5年是我们奋斗的黄金时期&#xff0c;如果此时还…

《阿里工程师的修养》:探究他们修的是什么

前言 最近有空闲的时候会在阿里藏金阁看书&#xff0c;偶然看到一本《阿里工程师的修养》&#xff0c;两眼放光&#xff0c;彷佛周星驰里面那个老伯对着一个拿着棒棒糖&#x1f36d;流鼻涕的小孩讲&#xff1a;我看你骨骼惊奇&#xff0c;我这里有本书适合你。 没错&#xff0…

外汇交易的技术为什么有效?量化藏金阁

外汇交易的逻辑很简单&#xff0c;只要在时间内成功预判汇价的走势方向即可成功获利&#xff0c;我们都知道如果随便猜那么成功的概率只有50%&#xff0c;是无法获利的&#xff0c;因此必须透过技术分析的手段交易&#xff0c;今天就跟大家聊聊为什么这些外汇技术有效。 1统计与…

mysql5.5不能远程_SQLServer2005不允许远程连接解决方法-数据库-藏金阁

刚刚安装的数据库系统&#xff0c;按照默认安装的话&#xff0c;很可能在进行远程连接时报错&#xff0c;通常是错误:"在连接到 SQL Server 2005 时&#xff0c;在默认的设置下 SQL Server 不允许进行远程连接可能会导致此失败。 (provider: 命名管道提供程序, error: 40 …

OCR二次开发宝典:飞桨联合多家企业和高校发布《OCR产业范例20讲》

文字识别&#xff08;Optical Character Recognition&#xff0c;OCR&#xff09;作为AI领域发展较成熟的一种技术方向&#xff0c;已经在各种产业场景得到落地应用。除了文档电子化、卡证识别等典型的应用场景&#xff0c;还存在大量长尾场景&#xff0c;如工业场景的PCB文字识…

程序员学炒股(3) 个股和大盘的关系之二

有了前一节的基础&#xff0c;这一节就简单多了&#xff0c;无非就是把所有股票遍历一下&#xff0c;我这里为了代码简单起见&#xff0c;就没有考虑停牌天数的影响。 下面就直接上代码了&#xff0c;我这里只是计算了一下沪市所有股票与上证指数的关系。 using System; using …

干货-任正非号召华为员工学习:认识5G,发展5G

华为创始人任正非近日签发的一份电子邮件号召华为全体员工学习一份名为《认识5G&#xff0c;发展5G》的PPT&#xff0c;该PPT是根据王喜文博士的文章缩编的&#xff0c;而王喜文博士是国内第一本“工业4.0”方面专著的作者。 该PPT主要围绕以下几个方面展开&#xff1a; 什么…

任正非号召华为员工学习的一份5G PPT(附下载)

今天&#xff0c;华为创始人任正非签发的一份邮件曝光。该邮件公布了一份名为《认识5G&#xff0c;发展5G》的PPT&#xff0c;号召供公共关系、接待经理、非市场与技术人员学习。 原来&#xff0c;这份PPT就是根据我们之前分享给大家的来自王喜文博士的《5G为人工智能与智能制造…

干货!任正非对话美国科技思想家,都说了些什么

https://www.toutiao.com/a6703424788846608903/ 2019-06-17 17:11:48 2019年6月17日下午2点&#xff0c;华为创始人兼CEO任正非在深圳与数字时代三大思想家的其中两位&#xff0c;《福布斯》著名撰稿人乔治吉尔德和美国《连线》杂志专栏作家尼古拉斯内格罗蓬特&#xff08;也…

华为又收天才少女,进华为的标准是什么?

昨天在华为官网刷到一个蛮有意思的事情&#xff0c;华为官网显示年仅 22 岁的瓦莱里娅 里亚布奇科娃&#xff0c;已正式加入华为俄罗斯下诺夫哥罗德研究所&#xff0c;任职高级工程师&#xff0c;从事智能计算应用加速技术方面的研究。行外的朋友可能不了解&#xff0c;这个女…

任正非,就会折腾

“兄弟们&#xff0c;好好干&#xff0c;未来的电信市场&#xff0c;华为三分天下有其一。” “兄弟们&#xff0c;好好地干&#xff01;我们的市场前景广阔得很&#xff0c;到那时大家的钱多得不得了&#xff0c;多到什么程度呢&#xff1f;就是钱在衣柜里面装不下&#xff0…

任正非与美国思想家的咖啡对话全文

来源&#xff1a;华为心声、蓝血研究 美国的所作所为促成了华为的觉醒&#xff0c;现在是华为的“人造卫星”时代&#xff01; ——尼古拉斯尼葛洛庞帝 美国学者与任正非的咖啡对话 2019年6月17日 田薇&#xff1a;“一杯咖啡吸收宇宙能量”&#xff0c;我是田薇。有人说&#…

【微信机器人】可做自动回复,自动接收转账,群聊机器人。

前言&#xff1a; 目前市面上的微信机器人项目少之又少&#xff0c;并且大多数的不可用。比如用抓取网页微信接口&#xff0c;但大多数人的账号没有使用网页微信的权限。又或者价格昂贵&#xff0c;如使用微信pad协议。于是便开发一个通过Hook微信的DLL文件&#xff0c;修改其…

计算机软件著作权法保护的内容不包括,计算机著作权保护法中的软件著作权包括哪些权限...

二、侵犯软件著作权 (软件的标准是什么&#xff1f;软件著作权不一定要注册&#xff0c;3.侵犯软件著作权的刑事处罚标准是什么&#xff1f;其实除了软件著作权&#xff0c;扩展阅读:计算机软件著作权如何申请注册&#xff0c;(作品没收侵权行为复制品&#xff0c;软件著作权注…

Python推送消息到钉钉群(从定义钉钉机器人到开发部署测试)

一、任务需求 此次的任务需求相对简单&#xff0c;思路也很清晰。就是从生产的库中读取数据&#xff0c;定时推送到钉钉群中用以消息通知&#xff0c;整个过程可以了解到如何自定义钉钉机器人、如何在Linux环境下部署python脚本并设置定时任务。 二、自定义钉钉机器人 1. 首…

创建钉钉群聊机器人,使用Python发送消息,使用DolphinScheduler发送告警

文章目录 获取自定义机器人Webhook使用Python发送消息使用curl发送消息使用DolphinScheduler发送告警 获取自定义机器人Webhook 1.1、创建群&#xff08;然后将别人移出群聊&#xff09; 1.2、单击群设置 > 智能群助手 1.3、在机器人管理页面选择自定义机器人 1.4、输入…