随着 Android 11 开发者预览版的发布,我们同时也推出了 Android 11 系统映像。新的系统映像在执行 ARM 二进制文件方面取得了显著的性能提升。此前,依赖 ARM 库且无法构建 x86 版本应用的开发者只能使用完整的 ARM 模拟系统映像 (其速度远低于在 x86 设备上运行 x86 系统映像) 或者实体机。Android 11 系统映像能够在不影响整个系统的前提下,直接将 ARM 指令转换成 x86 指令。开发者无需搭建高负载的 ARM 环境即可执行 ARM 二进制文件并进行测试。
ThinkPad-T440安装Windows 7(USB 3.0)
ThinkPad-T440安装Windows 7的时候,使用USB外置光驱安装系统,由于系统光盘没有集成USB 3.0的驱动,导致在安装过程中,出现错误,错误信息如下图所示:
如何激活机器自带正版Windows10或Office/查询Windows内置序列号
1、全新机器,第一次启动,连接上网。通常自动激活Windows10,Win10激活后,马上运行Office,据说因服务器原因很多机油无法立即激活,过一小会(几分钟)后就可以了。
2、 通用手工激活方法,正确输入正版软件的系列号。 该系列号位于机器Bios内,且不会因官方Bios升级取消。
不会查Bios的童鞋,请下载Produkey(绿色软件)自行查询(目前测试ThinkPad T440是可以的)
Spring Boot+MyBatis+SQLite配置
Spring Boot+MyBatis+SQLite配置例子参考下面
ubuntu 18.04完整安装配置WordPress 5.5.3并调优
|
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 |
$ sudo apt-get install apache2 $ sudo apt-get install mysql-server $ sudo apt-get install mysql-client $ sudo apt-get install php7.2 $ sudo apt-get install php-gd $ sudo apt install php-mbstring $ sudo apt install php-dom # 如果使用了WP-Statistics统计插件,需要安装依赖 $ sudo apt-get install php7.2-curl $ sudo apt-get install php7.2-bcmath # PHP Zip支持,提高网络以及内存压缩工具,提升性能 $ sudo apt-get install php-zip # PHP图像处理支持,imagick替代默认的GD图像处理,提升图像处理性能 $ sudo apt install php-imagick # 默认imagick是不启用的,需要手工开启 $ sudo phpenmod imagick $ sudo a2dismod mpm_prefork $ sudo a2enmod mpm_event $ sudo apt-get install libapache2-mod-fastcgi php7.2-fpm $ sudo service php7.2-fpm restart $ sudo a2enmod actions fastcgi alias proxy_fcgi $ sudo apt-get install php-mysql # 启用 Rewrite 模块,我们后续的WP Super Cache需要这个模块的支持 $ sudo a2enmod rewrite $ sudo service apache2 restart # 我们以root连接数据库,我们需要手工创建数据库,否则会出现如下错误: # “我们能够连接到数据库服务器(这意味着您的用户名和密码正确),但未能选择wordpress数据库。” $ mysql -u root -p -e "create database wordpress;" $ cd /var/www $ sudo chown -R www-data:www-data wordpress |
WordPress配置文件
|
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 |
<VirtualHost *:80> # The ServerName directive sets the request scheme, hostname and port that # the server uses to identify itself. This is used when creating # redirection URLs. In the context of virtual hosts, the ServerName # specifies what hostname must appear in the request's Host: header to # match this virtual host. For the default virtual host (this file) this # value is not decisive as it is used as a last resort host regardless. # However, you must set it for any further virtual host explicitly. #ServerName www.example.com ServerAdmin webmaster@localhost #DocumentRoot /var/www/html DocumentRoot /var/www/wordpress <Directory /var/www/wordpress> #Options Indexes FollowSymLinks MultiViews Options FollowSymLinks MultiViews AllowOverride All # Apache 2.2 # FCGIWrapper /usr/bin/php5-cgi .php # AddHandler fcgid-script .php # Options ExecCGI SymLinksIfOwnerMatch # Apache 2.4.10 <FilesMatch \.php$> SetHandler "proxy:unix:/run/php/php7.2-fpm.sock|fcgi://localhost" </FilesMatch> Order allow,deny allow from all </Directory> # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, # error, crit, alert, emerg. # It is also possible to configure the loglevel for particular # modules, e.g. #LogLevel info ssl:warn ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to # include a line for only one particular virtual host. For example the # following line enables the CGI configuration for this host only # after it has been globally disabled with "a2disconf". #Include conf-available/serve-cgi-bin.conf </VirtualHost> # vim: syntax=apache ts=4 sw=4 sts=4 sr noet |
启用Apache2的cache,expire,gzip模块,加强服务器性能
|
1 2 3 4 5 6 7 |
$ sudo a2enmod cache $ sudo a2enmod expires $ sudo a2enmod deflate $ sudo service apache2 restart |
PHP-FPM进程数的设定,提高响应速度,解决页面加载时候缓慢的问题(如果感觉打开页面缓慢,建议调整下面的配置,这种情况尤其在访问量不大的时候会出现短暂的耗时增大,原因就是php进程被临时创建导致延迟增大)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 增大最大并发进程数量,默认为5 根据需要增大 $ sudo sed -i "s/^pm.max_children = 5/pm.max_children = 20/g" /etc/php/7.2/fpm/pool.d/www.conf # 增大系统启动初始化的进程数量,默认为 1 根据需要增大 $ sudo sed -i "s/^pm.start_servers = 1/pm.start_servers = 10/g" /etc/php/7.2/fpm/pool.d/www.conf # 增大保留的最低空闲进程数量,默认为 2 根据需要增大 $ sudo sed -i "s/^pm.min_spare_servers = 2/pm.min_spare_servers = 10/g" /etc/php/7.2/fpm/pool.d/www.conf # 增大保留的最高空闲进程数量,默认为 3 根据需要增大 $ sudo sed -i "s/^pm.max_spare_servers = 3/pm.max_spare_servers = 20/g" /etc/php/7.2/fpm/pool.d/www.conf # 配置进程在处理多少个请求之后就退出,避免某些操作导致内存泄漏,及时进行资源回收操作 $ sudo sed -i "s/^#pm.max_requests = 500/pm.max_requests = 5000/g" /etc/php/7.2/fpm/pool.d/www.conf |
启用PHP7的opcache配置
|
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 |
######避免PHP信息暴露在http头中 expose_php = Off ######在关闭display_errors后开启PHP错误日志(路径在php-fpm.conf中配置) log_errors = On ######开启opcache [opcache] ; Determines if Zend OPCache is enabled ;opcache.enable=0 opcache.enable=1 ; The OPcache shared memory storage size. ;opcache.memory_consumption=128 opcache.memory_consumption=256 ; The maximum number of keys (scripts) in the OPcache hash table. ; Only numbers between 200 and 1000000 are allowed. ;opcache.max_accelerated_files=10000 opcache.max_accelerated_files=1000000 ; When disabled, you must reset the OPcache manually or restart the ; webserver for changes to the filesystem to take effect. ;opcache.validate_timestamps=1 opcache.validate_timestamps=3000 |
重启服务
|
1 |
$ sudo service php7.2-fpm restart |
参考链接
HttpClient利用MultipartEntityBuilder取代MultipartEntity上传图片文件到服务器
注意:在HttpClient4.3之后,原来的上传文件方法MultipartEntity已经不建议使用,现替换成新的httpmime下面的MultipartEntityBuilder。
需要添加httpclient-4.5.jar、httpmime-4.5.jar两个包
maven添加:
|
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> |
下面我们对比一下MultipartEntity和MultipartEntityBuilder的使用区别:
MultipartEntityBuilder:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; HttpPost httppost = new HttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //设置浏览器兼容模式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); //设置请求的编码格式 builder.setCharset(Consts.UTF_8); builder.setContentType(ContentType.MULTIPART_FORM_DATA); //添加文件 builder.addBinaryBody("file", file); HttpEntity reqEntity = builder.build(); httppost.setEntity(reqEntity); httpResponse = httpClient.execute(httppost); |
MultipartEntity:
|
1 2 3 4 5 6 7 8 9 10 |
//传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; HttpPost httppost = new HttpPost(url); FileBody filebody = new FileBody(file); MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8")); entity.addPart("file", filebody); httppost.setEntity(entity); |
直接上替换后的测试代码:
|
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 94 95 |
package com.test.util; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.Iterator; import java.util.Map; public class HttpPostUploadUtil { /** * @param args */ public static void main(String[] args) { String filePath = "D:\\Test\\0.jpg"; String url = "http://127.0.0.1:8080/FastFds"; //调用上传方法 String backInfo = uploadPost(url, filePath); if(StringUtils.isNotBlank(backInfo)){ //转json数据 JSONObject json = JSONObject.fromObject(backInfo); if(!json.isEmpty()){ //数据处理 String value = json.getString("test"); System.out.println(value); } } } /** * 上传图片/文件 * @param url * @param filePath * @return String 用于转json或者其他信息 */ public static String uploadPost(String url, String filePath) { String requestJson = ""; //传入参数可以为file或者filePath,在此处做转换 File file = new File(filePath); CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = null; try { HttpPost httppost = new HttpPost(url); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //设置浏览器兼容模式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); //设置请求的编码格式 builder.setCharset(Consts.UTF_8); builder.setContentType(ContentType.MULTIPART_FORM_DATA); //添加文件 builder.addBinaryBody("file", file); HttpEntity reqEntity = builder.build(); httppost.setEntity(reqEntity); httpResponse = httpClient.execute(httppost); int backCode = httpResponse.getStatusLine().getStatusCode(); if(backCode == HttpStatus.SC_OK){ HttpEntity httpEntity = httpResponse.getEntity(); byte[] json= EntityUtils.toByteArray(httpEntity); requestJson = new String(json, "UTF-8"); //关闭流 EntityUtils.consume(httpEntity); return requestJson; } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //释放资源 try { httpClient.close(); httpResponse.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } } |
继续阅读HttpClient利用MultipartEntityBuilder取代MultipartEntity上传图片文件到服务器
正则表达式 - 字符匹配不以某字段开头或者结尾
正则表达式 - 字符匹配不以某字段开头或者结尾
最近有一个需求,要求是判断某个字符串不以什么开头。然后就开始探索这个不以什么开头和不易什么结尾的正则怎么写,why?
不以某字符串开头
这里需要提一个概念叫 否定式前项匹配 这个东东。
向前匹配
根据匹配的字符序列后面存在一个特定的字符序列或者不存在一个特定的序列来决定是否匹配。对于向前匹配,出现在指定项后面的字符序列不会被正则表达式返回。
这里说后面存在一个特定字符序列, 也称之为肯定式向前查找
不存在一个特定的序列,也称之为否定式向前查找
这两个概念后续再深入
开始解决这个问题不以某字符串开头:
|
1 |
^(?!str) |
这里使用了下面几个元符号:
^判断是否是开头?!这里是否定向前查询
示例:
- 不以test 开头字符串
|
1 |
/^(?!test).*/ |
Spring Boot实现异步任务
Spring Boot中的另外一个任务:异步任务。所谓异步任务,其实就是异步执行程序,有些时候遇到一些耗时的的任务,如果一直卡等待,肯定会影响其他程序的执行,所以就让这些程序需要以异步的方式去执行。那么下面就来介绍Spring Boot 如何实现异步任务。
一、使用注解@EnableAsync 开启异步调用方法
在application启动类中,加上@EnableAsync注解,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 |
package com.weiz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import tk.mybatis.spring.annotation.MapperScan; @SpringBootApplication //扫描 mybatis mapper 包路径 @MapperScan(basePackages = "com.weiz.mapper") //扫描 所有需要的包, 包含一些自用的工具类包 所在的路径 @ComponentScan(basePackages = {"com.weiz","org.n3r.idworker"}) //开启定时任务 @EnableScheduling //开启异步调用方法 @EnableAsync public class SpringBootStarterApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStarterApplication.class, args); } } |
二、创建异步执行类,定义@Component及@Async组件
创建com.weiz.tasks包,在tasks包里增加AsyncTask 异步任务类,加上@Component 注解,然后在需要异步执行的方法前面加上@Async注解,这样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 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.weiz.tasks; import java.util.concurrent.Future; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; @Component public class AsyncTask { @Async public Future<Boolean> doTask11() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println("任务1耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask22() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(700); long end = System.currentTimeMillis(); System.out.println("任务2耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask33() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(600); long end = System.currentTimeMillis(); System.out.println("任务3耗时:" + (end - start) + "毫秒"); return new AsyncResult<>(true); } } |
说明:@Async 加上这个注解,就表示该方法是异步执行方法。
三、调用
创建一个DoTask调用类,我们看看这几个方法,是怎么执行的:
|
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 |
package com.weiz.tasks; import java.util.concurrent.Future; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("tasks") public class DoTask { @Autowired private AsyncTask asyncTask; @RequestMapping("test1") public String test1() throws Exception { long start = System.currentTimeMillis(); Future<Boolean> a = asyncTask.doTask11(); Future<Boolean> b = asyncTask.doTask22(); Future<Boolean> c = asyncTask.doTask33(); while (!a.isDone() || !b.isDone() || !c.isDone()) { if (a.isDone() && b.isDone() && c.isDone()) { break; } } long end = System.currentTimeMillis(); String times = "任务全部完成,总耗时:" + (end - start) + "毫秒"; System.out.println(times); return times; } } |
SpringBoot如何实现一个实时更新的进度条的示例代码
前言
导入excel表格批量修改状态,期间如果发生错误则所有数据不成功,为了防止重复提交,做一个类似进度条的东东。
那么下面我会结合实际业务对这个功能进行分析和记录。
正文
思路
前端使用bootstrap,后端使用SpringBoot分布式到注册中心,原先的想法是导入表格后异步调用修改数据状态的方法,然后每次计算修改的进度然后存放在session中,前台jquery写定时任务访问获取session中的进度,更新进度条进度和百分比。但是这存在session在服务间不共享,跨域问题。那么换一个方式存放,存放在redis中,前台定时任务直接操作获取redis的数据。
实施
进度条
先来看一下bootstrap的进度条
|
1 2 3 4 5 6 7 |
<div class="progress progress-striped active"> <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 40%;"> 40% </div> </div> |
进度条更新主要更新style="width: 40%;"的值即可,div里面的40%可以省略,无非时看着明确。
可以考虑将进度条放入弹出层。
定时任务
|
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 |
//点击确认导入执行此方法 function bulkImportChanges() { //获取批量操作状态文件 var files = $("#importChanges").prop("files"); var changesFile = files[0]; var formData = new FormData(); formData.append("importFile",changesFile); $.ajax({ type : 'post', url : "/risk/bulk***es", data : formData, processData : false, //文件ajax上传要加这两个的,要不然上传不了 contentType : false, // success : function(obj) { //导入成功 if (obj.rspCode == "00") { //定时任务获取redis导入修改进度 var progress = ""; var timingTask = setInterval(function(){ $.ajax({ type: 'post', url: "/risk/t***k", dataType : 'json', success: function(result) { progress = result.value; if (progress != "error"){ var date = progress.substring(0,6); //这里更新进度条的进度和数据 $(".progress-bar").width(parseFloat(date)+"%"); $(".progress-bar").text(parseFloat(date)+"%"); } } }); //导入修改完成或异常(停止定时任务) if (parseInt(progress)==100 || progress == "error") { //清除定时执行 clearInterval(timingTask); $.ajax({ type: 'post', url: "/risk/de***ess", dataType : 'json', success: function(result) { $("#bulkImportChangesProcessor").hide(); if (parseInt(progress) == 100) { alert("批量导入修改状态成功"); } if (progress == "error") { alert("批量导入修改状态失败"); } //获取最新数据 window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ; } }); } }, 1000) }else { $("#bulkImportChangesProcessor").hide(); alert(obj.rspMsg); window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ; } } }); } |
解释:点击确认导入文件后成功后开启定时任务每一秒(一千毫秒)访问一次后台获取redis存放的进度,返回更新进度条,如果更新完成或者更新失败(根据后台返回的数据决定)则停止定时任务显示相应的信息并刷新页面。获取最新数据。
后台控制层
|
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 |
/** * 退单管理批量修改状态导入文件 * @param importFile * @return */ @ResponseBody @RequestMapping("/bulk***es") public Map<String,Object> bulk***es(MultipartFile importFile){ log.info("退单管理批量修改状态导入文件,传入参数:"+importFile); Map<String,Object> map = new HashMap<>(); List<Bulk***esEntity> fromExcel = null; try{ //使用工具类导入转成list String[] header = {"sy***um","t***mt","ha***ult","re***nd","sy***nd","r**k"}; fromExcel = importExcelUtil.importDataFromExcel(importFile, header, BulkImportChangesEntity.class); if (fromExcel.size()==0){ map.put("rspCode","99"); map.put("rspMsg","导入数据不能为空"); return map; } }catch (Exception e){ map.put("rspCode","99"); map.put("rspMsg","导入操作表失败,请注意数据列格式"); return map; } try { //这里会对list集合中的数据进行处理 log.info("调用服务开始,参数:"+JSON.toJSONString(fromExcel)); //String url = p4_zuul_url+"/***/ri***eat/bu***nges"; String url = p4_zuul_url+"/***-surpass/ri***eat/bu***nges"; String result = HttpClientUtil.doPost(url,JSON.toJSONString(fromExcel)); log.info("调用服务结束,返回数据:"+result); if (result != null){ map = JSONObject.parseObject(result, Map.class); log.info("批量修改状态导入:"+JSON.toJSONString(map)); } }catch (Exception e){ map.put("rspCode","99"); map.put("rspMsg","导入操作表失败"); log.info("bu***es exception",e); return map; } return map; } /** * 获取退单管理批量修改状态导入文件进度条进度 * @return */ @ResponseBody @RequestMapping("/t***sk") public Map<String,Object> t***sk(){ Map<String,Object> map = new HashMap<>(); //获取redis值 String progress = HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/get?key=progressSchedule"); if (progress != null){ map = JSONObject.parseObject(progress, Map.class); log.info("进度条进度:"+JSON.toJSONString(map)); map.put("progressSchedule",progress); }else { HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule"); } return map; } /** * 清除redis进度条进度 * @return */ @ResponseBody @RequestMapping("/de***ess") public Map<String,Object> de***ess(){ Map<String,Object> map = new HashMap<>(); String progress = HttpClientUtil.doGet( p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule"); if (progress != null){ map = JSONObject.parseObject(progress, Map.class); log.info("返回数据:"+JSON.toJSONString(map)); } return map; } |
导入时调用第一个bulk***es方法,定时任务调用t***sk方法,导入完成或发生错误调用de***ess方法删除redis数据,避免占用资源。
服务层
|
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 |
@Async//开启异步 @Transactional(rollbackFor = Exception.class)//事务回滚级别 @Override public void bulkImportChanges(List<BulkImportChangesParam> list) { //初始化进度 Double progressBarSchedule = 0.0; redisClient.set("progressSchedule", progressBarSchedule + "");//存redis try { for (int i = 1; i <= list.size(); i++) { RiskRetreatEntity entity = riskRetreatMapper.selectRetreatListBySysRefNum(list.get(i-1).getSysRefNum()); if (entity == null){ //查询结果为空直接进行下次循环不抛出 continue; } //实体封装 ··· //更新 riskRetreatMapper.updateRetreatByImport(entity); //计算修改进度并存放redis保存(1.0 / list.size())为一条数据进度 progressBarSchedule = (1.0 / list.size()) * i*100; redisClient.set("progressSchedule", progressBarSchedule+""); if (i==list.size()){ redisClient.set("progressSchedule", "100"); } } }catch (Exception e){ //当发生错误则清除缓存直接抛出回滚 redisClient.set("progressSchedule","error"); log.info("导入更新错误,回滚"); log.info("bulkImportChanges exception:",e); throw e; } } |
每更新一条数据存放进度,当发生错误则进行回滚。如果开启异步则需要在启动类添加注解@EnableAsync。
|
1 2 3 4 5 6 7 |
@EnableAsync ···//其他注解 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
认识ARM64汇编
之前说过学习汇编就是学习寄存器和指令,查看代码请连接真机。
寄存器
在arm64汇编中寄存器是64bit的,使用X[n]表示,低32位以w[n]表示

在64位架构中有31个64位的通用寄存器。
可以通过register read查看
|
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 |
(lldb) register read General Purpose Registers: x0 = 0x0000000000000001 x1 = 0x00000002805d8a20 x2 = 0x00000002837e7980 x3 = 0x00000002805d8a20 x4 = 0x0000000000000001 x5 = 0x0000000000000001 x6 = 0x0000000100d54000 x7 = 0x0000000000000000 x8 = 0x0000200000000000 x9 = 0x000161a1d63f7945 (0x00000001d63f7945) (void *)0x01d63f7cb0000001 x10 = 0x0000000000000000 x11 = 0x000000000000006d x12 = 0x000000000000006d x13 = 0x0000000000000000 x14 = 0x0000000000000000 x15 = 0x00000001ca634e6d "touchesBegan:withEvent:" x16 = 0x000000019c4cd47c libobjc.A.dylib`objc_storeStrong x17 = 0x0000000000000000 x18 = 0x0000000000000000 x19 = 0x0000000100f17810 x20 = 0x00000002837e7920 x21 = 0x00000002805d8a20 x22 = 0x00000001ca634e6d "touchesBegan:withEvent:" x23 = 0x0000000100e11a30 x24 = 0x00000002837e7980 x25 = 0x0000000100e11a30 x26 = 0x00000002805d8a20 x27 = 0x00000001ca62d900 "countByEnumeratingWithState:objects:count:" x28 = 0x00000002805d8a20 fp = 0x000000016f47d730 lr = 0x00000001009866dc ArmAssembly`-[ViewController touchesBegan:withEvent:] + 84 at ViewController.m:38 sp = 0x000000016f47d720 pc = 0x0000000100986720 ArmAssembly`foo1 + 16 at ViewController.m:46 cpsr = 0x80000000 (lldb) |