前言

有一张客户信息表,数据量很大。需要把这些记录调用某个接口(这个接口一次只能查询10个客户信息),然后把接口返回信息解析保存到另一个表里。

客户信息表(cust_info)如下:

id cust_id status remark input_time update_tiem
1 20191111000001 2019-11-23 10:45:04 2019-11-23 10:45:04
2 20191111000002 2019-11-23 10:45:04 2019-11-23 10:45:04
3 20191111000003 2019-11-23 10:45:04 2019-11-23 10:45:04
4 20191111000004 2019-11-23 10:45:04 2019-11-23 10:45:04
5 20191111000005 2019-11-23 10:45:04 2019-11-23 10:45:04

解析后要保存的表(cust_detail)如下:

id cust_id city name tel age input_time update_time
1 20191111000001 北京 张三 1877778872 12 2019-11-23 10:45:04 2019-11-23 10:45:04
2 20191111000002 北京 张三 1877778872 12 2019-11-23 10:45:04 2019-11-23 10:45:04
3 20191111000003 北京 张三 1877778872 12 2019-11-23 10:45:04 2019-11-23 10:45:04
4 20191111000004 北京 张三 1877778872 12 2019-11-23 10:45:04 2019-11-23 10:45:04
5 20191111000005 北京 张三 1877778872 12 2019-11-23 10:45:04 2019-11-23 10:45:04
第一版

思路:
使用线程池,结合redis来实现。每次固定从cust_info表取30条未处理的记录,按10等分给三个线程。用redis来统计线程是否执行完毕,在redis中存一个变量值为开启的线程数。当子线程执行完毕时将redis中的该变量减一。while循环发现该变量值变为0时说明子线程全部执行完毕,然后再取30条未处理的数据,重复上面的步骤,直至完成。子线程在查询完某个客户时会更新cust_info表的status字段值为1,表示该客户已经被查询过了。

缺点:

  1. 需要依赖redis。
  2. 每次循环要等所以有的线程执行完毕后,再进入下一次循环,线程利用不充分。

主要文件详情:

  1. CustThreadPoolExecutor.java 线程池
  2. CustQueryOneThread.java 线程类
  3. CustInfoOneServiceImpl.java 业务逻辑类

每次取数据的mybatis的 xml文件

    <select id="selectCustInfoList" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from cust_infowhere status = 0 or status is null</select>

CustThreadPoolExecutor.java

package com.lh.service.task;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class CustThreadPoolExecutor {private static ThreadPoolExecutor pool;private static void init() {pool = new ThreadPoolExecutor(3,6,1000,TimeUnit.MICROSECONDS,new ArrayBlockingQueue<Runnable>(30));}private CustThreadPoolExecutor() {pool = new ThreadPoolExecutor(3,6,1000,TimeUnit.MICROSECONDS,new ArrayBlockingQueue<Runnable>(30));}public static void execute(Runnable runnable) {if (pool == null) {init();}pool.execute(runnable);}
}

CustQueryOneThread.java

package com.lh.service.task;import com.alibaba.fastjson.JSON;
import com.lh.dao.sys.CustDetailMapper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.entity.CustDetail;
import com.lh.entity.CustInfo;
import com.lh.entity.enums.RedisKeyEnum;
import com.lh.utils.RedisUtil;
import com.lh.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Slf4j
public class CustQueryOneThread implements Runnable {private List<CustInfo> data;private Map<String, CustInfo> map;private SqlSessionFactory sqlSessionFactory;private RedisUtil redisUtil;public CustQueryOneThread(List<CustInfo> data) {this.data = data;this.map = new HashMap<>();this.sqlSessionFactory = SpringUtil.getBean(SqlSessionFactory.class);this.redisUtil = SpringUtil.getBean(RedisUtil.class);}@Overridepublic void run() {try {// 1、组装参数String custIds = this.before();// 2、调用接口 我们这里简单模拟String result = this.runing(custIds);// 3、解析返回保存记录this.after(result);} catch (Exception e) {e.printStackTrace();log.error("查询客户信息发生异常!e={}", e);} finally {//线程执行完毕 计数减一redisUtil.incrBy(RedisKeyEnum.CUST_QUERY_THREAD_COUNT.getCode(), -1);}}/*** 组装接口调用入参** @return*/private String before() {return data.stream().map(a -> {map.put(a.getCustId(), a);return a.getCustId();}).collect(Collectors.joining(","));}/*** 模拟调用外部接口返回Json 数据** @return*/private String runing(String request) {List<CustDetail> list = Arrays.stream(request.split(",")).map(a -> {CustDetail custDetail = new CustDetail();custDetail.setCustId(a);custDetail.setAge(12);custDetail.setCity("北京");custDetail.setTel("17788998880");return custDetail;}).collect(Collectors.toList());return JSON.toJSONString(list);}/*** 解析接口返回并保存结果** @param result*/private void after(String result) {SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);try {CustDetailMapper custDetailMapper = session.getMapper(CustDetailMapper.class);CustInfoMapper custInfoMapper = session.getMapper(CustInfoMapper.class);List<CustDetail> list = JSON.parseArray(result, CustDetail.class);for (CustDetail custDetail : list) {custDetailMapper.insertSelective(custDetail);CustInfo custInfo = map.get(custDetail.getCustId());custInfo.setStatus(Byte.parseByte("1"));custInfo.setRemark("处理成功");custInfoMapper.updateByPrimaryKeySelective(custInfo);}session.commit();session.clearCache();} catch (Exception e) {e.printStackTrace();log.error("保存接口返回发生异常!e={}", e);session.rollback();} finally {session.close();}}}

CustInfoOneServiceImpl.java

package com.lh.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.entity.CustInfo;
import com.lh.entity.enums.RedisKeyEnum;
import com.lh.service.CustInfoService;
import com.lh.service.task.CustQueryOneThread;
import com.lh.service.task.CustThreadPoolExecutor;
import com.lh.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;@Slf4j
@Service("custInfoOneServiceImpl")
public class CustInfoOneServiceImpl implements CustInfoService {private static final int PAGE_SIZE = 30;private static final int QUERY_SIZE = 10;@Resourceprivate CustInfoMapper custInfoMapper;@Resourceprivate RedisUtil redisUtil;@Overridepublic void queryJob() {try {log.info("==============cust query task begin!=================");redisUtil.set(RedisKeyEnum.CUST_QUERY_THREAD_COUNT.getCode(), "0");while (true) {String s = redisUtil.get(RedisKeyEnum.CUST_QUERY_THREAD_COUNT.getCode());if ("0".equals(s)) {this.query();} else if ("-1".equals(s)) {break;} else {Thread.sleep(1000);}}log.info("==============cust query task end !=================");} catch (Exception e) {e.printStackTrace();log.error("==============cust query task exception! e={}=================", e);}}private void query() {Page page = PageHelper.startPage(1, PAGE_SIZE);List<CustInfo> list = custInfoMapper.selectCustInfoList();if (list == null || list.size() == 0) {redisUtil.set(RedisKeyEnum.CUST_QUERY_THREAD_COUNT.getCode(), "-1");return;}int size = list.size();int m = size / QUERY_SIZE;int n = size % QUERY_SIZE;int threadCount = m + (n > 0 ? 1 : 0);redisUtil.set(RedisKeyEnum.CUST_QUERY_THREAD_COUNT.getCode(), String.valueOf(threadCount));if (m > 0) {for (int i = 0; i < m; i++) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * i).limit(QUERY_SIZE).collect(Collectors.toList());CustThreadPoolExecutor.execute(new CustQueryOneThread(collect));}}if (n > 0) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * m).collect(Collectors.toList());CustThreadPoolExecutor.execute(new CustQueryOneThread(collect));}}
}
第二版

思路:
使用线程池,结合CountDownLatch来实现。每次固定从cust_info表取30条未处理的记录,按10等分给三个线程,用CountDownLatch来统计线程是否执行完毕。CountDownLatch在创建时传入开启的线程数。并将CountDownLatch对象传给子线程,子线程执行完毕时调用CountDownLatch的countDown()方法,此时主线程调用CountDownLatch的await()方法等待所有子线程执行完毕。当所有的子线程都调用完countDown()方法说明子线程全部执行完毕,然后主线程继续进入下一个循环。再取30条未处理的数据,重复上面的步骤,直至完成。子线程在查询完某个客户时会更新cust_info表的status字段值为1,表示该客户已经被查询过了。

优点:不需要依赖其他。
缺点: 每次循环要等所以有的线程执行完毕后,再进入下一次循环,线程利用不充分。

主要文件详情:

  1. CustQueryTwoThread.java 线程类
  2. CustInfoTwoServiceImpl.java 业务逻辑类

每次取数据的mybatis的 xml文件

    <select id="selectCustInfoList" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from cust_infowhere status = 0 or status is null</select><select id="selectCustInfoCount" resultType="java.lang.Integer">select count(1) from cust_info</select>

CustQueryTwoThread.java

package com.lh.service.task;import com.alibaba.fastjson.JSON;
import com.lh.dao.sys.CustDetailMapper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.entity.CustDetail;
import com.lh.entity.CustInfo;
import com.lh.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;@Slf4j
public class CustQueryTwoThread implements Runnable {private List<CustInfo> data;private Map<String, CustInfo> map;private CountDownLatch countDownLatch;private SqlSessionFactory sqlSessionFactory;public CustQueryTwoThread(List<CustInfo> data, CountDownLatch countDownLatch) {this.data = data;this.map = new HashMap<>();this.countDownLatch = countDownLatch;this.sqlSessionFactory = SpringUtil.getBean(SqlSessionFactory.class);}@Overridepublic void run() {try {// 1、组装参数String custIds = this.before();// 2、调用接口 我们这里简单模拟String result = this.runing(custIds);// 3、解析返回保存记录this.after(result);} catch (Exception e) {e.printStackTrace();log.error("查询客户信息发生异常!e={}", e);} finally {//线程执行完毕 释放countDownLatch.countDown();}}/*** 组装接口调用入参** @return*/private String before() {return data.stream().map(a -> {map.put(a.getCustId(), a);return a.getCustId();}).collect(Collectors.joining(","));}/*** 模拟调用外部接口返回Json 数据** @return*/private String runing(String request) {List<CustDetail> list = Arrays.stream(request.split(",")).map(a -> {CustDetail custDetail = new CustDetail();custDetail.setCustId(a);custDetail.setAge(12);custDetail.setCity("北京");custDetail.setTel("17788998880");custDetail.setName("小花");return custDetail;}).collect(Collectors.toList());return JSON.toJSONString(list);}/*** 解析接口返回并保存结果** @param result*/private void after(String result) {SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);try {CustDetailMapper custDetailMapper = session.getMapper(CustDetailMapper.class);CustInfoMapper custInfoMapper = session.getMapper(CustInfoMapper.class);List<CustDetail> list = JSON.parseArray(result, CustDetail.class);for (CustDetail custDetail : list) {custDetailMapper.insertSelective(custDetail);CustInfo custInfo = map.get(custDetail.getCustId());custInfo.setStatus(Byte.parseByte("1"));custInfo.setRemark("处理成功");custInfoMapper.updateByPrimaryKeySelective(custInfo);}session.commit();session.clearCache();} catch (Exception e) {e.printStackTrace();log.error("保存接口返回发生异常!e={}", e);session.rollback();} finally {session.close();}}}

CustInfoTwoServiceImpl.java

package com.lh.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.entity.CustInfo;
import com.lh.service.CustInfoService;
import com.lh.service.task.CustQueryTwoThread;
import com.lh.service.task.CustThreadPoolExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;@Slf4j
@Service("custInfoTwoServiceImpl")
public class CustInfoTwoServiceImpl implements CustInfoService {private static final int PAGE_SIZE = 30;private static final int QUERY_SIZE = 10;@Resourceprivate CustInfoMapper custInfoMapper;@Overridepublic void queryJob() {try {log.info("==============cust query task begin!=================");int i = custInfoMapper.selectCustInfoCount();int pageCount = i / PAGE_SIZE + (i % PAGE_SIZE > 0 ? 1 : 0);int flag = 1;while (flag <= pageCount) {Page page = PageHelper.startPage(1, PAGE_SIZE);List<CustInfo> list = custInfoMapper.selectCustInfoList();if (list == null || list.size() == 0) {break;}this.query(list);flag++;}log.info("==============cust query task end !=================");} catch (Exception e) {e.printStackTrace();log.error("==============cust query task exception! e={}=================", e);}}private void query(List<CustInfo> list) throws InterruptedException {int size = list.size();int m = size / QUERY_SIZE;int n = size % QUERY_SIZE;int threadCount = m + (n > 0 ? 1 : 0);CountDownLatch latch = new CountDownLatch(threadCount);if (m > 0) {for (int i = 0; i < m; i++) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * i).limit(QUERY_SIZE).collect(Collectors.toList());CustThreadPoolExecutor.execute(new CustQueryTwoThread(collect, latch));}}if (n > 0) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * m).collect(Collectors.toList());CustThreadPoolExecutor.execute(new CustQueryTwoThread(collect, latch));}// 主线程等待所以有子线程执行完毕再继续执行latch.await();}
}
第三版

思路:
使用线程池,和Semaphore 实现。Semaphore 初始化时设置线程数为3,将cust_info表按照id字段排序每次依次从cust_info表30条记录,按10等分给三个线程,在分给线程之前要先获取许可(semaphore.acquire()),有许可时才开启线程。当子线程在执行完毕后会释放许可,主线程这时会获取许可,把准备好的数据分给子线程。当一次循环执行完毕之后再依次向下取30条。重复上面的步骤,直至完成。

优点:不需要依赖其他。每当一个线程执行完毕,就可以开启一个新的线程。不用等到所有的线程执行完毕再继续。

主要文件详情:

  1. CustQueryThreeThread.java 线程类
  2. CustInfoThreeServiceImpl.java 业务逻辑类
    <select id="selectList" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from cust_info</select><select id="selectCustInfoCount" resultType="java.lang.Integer">select count(1) from cust_info</select>

CustQueryThreeThread.java

package com.lh.service.task;import com.alibaba.fastjson.JSON;
import com.lh.dao.sys.CustDetailMapper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.entity.CustDetail;
import com.lh.entity.CustInfo;
import com.lh.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;@Slf4j
public class CustQueryThreeThread implements Runnable {private List<CustInfo> data;private Map<String, CustInfo> map;private Semaphore semaphore;private SqlSessionFactory sqlSessionFactory;public CustQueryThreeThread(List<CustInfo> data, Semaphore semaphore) {this.data = data;this.map = new HashMap<>();this.semaphore = semaphore;this.sqlSessionFactory = SpringUtil.getBean(SqlSessionFactory.class);}@Overridepublic void run() {try {// 1、组装参数String custIds = this.before();// 2、调用接口 我们这里简单模拟System.out.println("sleep =====  begin");Thread.sleep(10000);System.out.println("sleep =====  end");String result = this.runing(custIds);// 3、解析返回保存记录this.after(result);} catch (Exception e) {e.printStackTrace();log.error("查询客户信息发生异常!e={}", e);} finally {//线程执行完毕 释放许可System.out.println("sleep ===== release ");semaphore.release();}}/*** 组装接口调用入参** @return*/private String before() {return data.stream().map(a -> {map.put(a.getCustId(), a);return a.getCustId();}).collect(Collectors.joining(","));}/*** 模拟调用外部接口返回Json 数据** @return*/private String runing(String request) {List<CustDetail> list = Arrays.stream(request.split(",")).map(a -> {CustDetail custDetail = new CustDetail();custDetail.setCustId(a);custDetail.setAge(12);custDetail.setCity("北京");custDetail.setTel("17788998880");custDetail.setName("小花");return custDetail;}).collect(Collectors.toList());return JSON.toJSONString(list);}/*** 解析接口返回并保存结果** @param result*/private void after(String result) {SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);try {CustDetailMapper custDetailMapper = session.getMapper(CustDetailMapper.class);CustInfoMapper custInfoMapper = session.getMapper(CustInfoMapper.class);List<CustDetail> list = JSON.parseArray(result, CustDetail.class);for (CustDetail custDetail : list) {custDetailMapper.insertSelective(custDetail);CustInfo custInfo = map.get(custDetail.getCustId());custInfo.setStatus(Byte.parseByte("1"));custInfo.setRemark("处理成功");custInfoMapper.updateByPrimaryKeySelective(custInfo);}session.commit();session.clearCache();} catch (Exception e) {e.printStackTrace();log.error("保存接口返回发生异常!e={}", e);session.rollback();} finally {session.close();}}}

CustInfoThreeServiceImpl.java

package com.lh.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lh.dao.sys.CustInfoMapper;
import com.lh.dao.sys.CustQueryRecordMapper;
import com.lh.entity.CustInfo;
import com.lh.entity.CustQueryRecord;
import com.lh.service.CustInfoService;
import com.lh.service.task.CustQueryThreeThread;
import com.lh.service.task.CustQueryTwoThread;
import com.lh.service.task.CustThreadPoolExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;@Slf4j
@Service("custInfoThreeServiceImpl")
public class CustInfoThreeServiceImpl implements CustInfoService {private static final int PAGE_SIZE = 30;private static final int QUERY_SIZE = 10;@Resourceprivate CustInfoMapper custInfoMapper;@Resourceprivate CustQueryRecordMapper custQueryRecordMapper;@Overridepublic void queryJob() {try {log.info("==============cust query task begin!=================");Semaphore semaphore = new Semaphore(3);int i = custInfoMapper.selectCustInfoCount();int pageCount = i / PAGE_SIZE + (i % PAGE_SIZE > 0 ? 1 : 0);int flag = 1;while (flag <= pageCount) {Page page = PageHelper.startPage(flag, PAGE_SIZE);List<CustInfo> list = custInfoMapper.selectList();if (list == null || list.size() == 0) {break;}this.query(list, semaphore);this.saveRecord(flag, list.size());flag++;}log.info("==============cust query task end !=================");} catch (Exception e) {e.printStackTrace();log.error("==============cust query task exception! e={}=================", e);}}/*** 查询** @param list* @param semaphore* @throws InterruptedException*/private void query(List<CustInfo> list, Semaphore semaphore) throws InterruptedException {int size = list.size();int m = size / QUERY_SIZE;int n = size % QUERY_SIZE;if (m > 0) {for (int i = 0; i < m; i++) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * i).limit(QUERY_SIZE).collect(Collectors.toList());// 获取许可semaphore.acquire();CustThreadPoolExecutor.execute(new CustQueryThreeThread(collect, semaphore));}}if (n > 0) {List<CustInfo> collect = list.stream().skip(QUERY_SIZE * m).collect(Collectors.toList());CustThreadPoolExecutor.execute(new CustQueryThreeThread(collect, semaphore));}}/*** 保存记录** @param page* @param size*/private void saveRecord(int page, int size) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String busiDate = sdf.format(new Date());CustQueryRecord record = new CustQueryRecord();record.setRecordId(busiDate + "_" + page);record.setPage(page);record.setBusiDate(busiDate);record.setRemark(String.valueOf(size));custQueryRecordMapper.insertSelective(record);}
}

新增了查询记录表,这里会把执行成功的记录和页号存在表里。方便异常后重启用。可以在方法开始的时候根据busi_date 来查询page最大值,如果存在从最大值+1开始执行。如果不存在就从第一页(page等于1)开始执行。注意获取最大值后要和根据和 pagecount (int pageCount = i / PAGE_SIZE + (i % PAGE_SIZE > 0 ? 1 : 0);)进行比较如果相等或者大于pagecount ;直接return。

id record_id page remark busi_date input_time update_time
1 2019-11-23_1 1 30 2019-11-23 2019-11-23 19:09:12 2019-11-23 19:09:12
2 2019-11-23_2 2 30 2019-11-23 2019-11-23 19:09:22 2019-11-23 19:09:22
3 2019-11-23_3 3 30 2019-11-23 2019-11-23 19:09:32 2019-11-23 19:09:32
4 2019-11-23_4 4 10 2019-11-23 2019-11-23 19:09:42 2019-11-23 19:09:42

Java多线程实现接口调用相关推荐

  1. java字典写实例,基于JAVA的新华字典接口调用代码实例

    基于JAVA的新华字典接口调用代码实例 接口描述:基于JA V A的新华字典接口调用代码实例 接口平台:聚合数据 import java.io.BufferedReader; import java. ...

  2. Java多线程实现异步调用

    在Java平台,实现异步调用的角色有如下三个角色:调用者. 提货单 .真实数据,一个调用者在调用耗时操作,不能立即返回数据时,先返回一个提货单 .然后在过一断时间后凭提货单来获取真正的数据.去蛋糕店买 ...

  3. Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:终于放弃了单调的swagger-ui了,选择了这款神器-knife4j个人原创100W+访问量博客:点击前往,查 ...

  4. Java多线程Callable接口

    Callable和Future出现的原因 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口.  这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果.  如 ...

  5. 新华字典java_基于JAVA的新华字典接口调用代码实例

    [java]代码库import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; ...

  6. Java后端feign接口调用

    现如今微服务架构十分流行,而采用微服务构建系统也会带来更清晰的业务划分和可扩展性.java如果使用微服务就离不开springcloud,我这里是把服务注册到nacos上,各个服务之间的调用使用feig ...

  7. java短信接口 调用_带你了解短信接口的调用

    查看接口说明 image.png 注册账号 注册成功的页面如下: image.png 设置短信内容的签名 比如:将短信签名设置为yzc image.png 查取短信接口密钥 image.png 设计J ...

  8. Java多线程 - Runnable接口和Callable接口的区别

    文章目录 1. Runnable接口实例 2. Callable接口原理 3. Callnable接口实例 4. FutureTask是什么? 5. 线程池中 submit() 和 execute() ...

  9. java短信接口调用

    1.在action中调用webservice package com.haikan.exam.action; import java.rmi.RemoteException; import java. ...

最新文章

  1. 7月关键词“科幻”:梁建章新书《永生之后》发售 人类寿命将达10000年?
  2. Center OS 5.5 下安装 和 配置 Tomcat 7
  3. 运行时修改数据库连接字符串(ConnectionString)
  4. RAID,LVM创建
  5. c# 水晶报表中处理TextObject
  6. 2020-07-15 CVPR2020 表示学习论文讨论(4) 笔记
  7. linux 查看磁盘分区,文件系统,使用情况的命令和相关工具介绍,Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍df...
  8. java final 方法重载_java方法重载和覆写的定义,static和final修饰符的讲解,java面试题...
  9. OpenCV——绘制基本图形
  10. gef 图形 如何禁止修改大小
  11. 60-124-340-源码-运行模式-Yarn-通过 YARN 的资源本地化技术减少 Flink 在 YARN 上的部署时间
  12. html焦点自动轮播幻灯片js,js实现幻灯片轮播图
  13. 数字通信学习笔记——自相关函数、随机信号
  14. 3D人体姿态估计总结
  15. java pfx_java读取pfx或P12格式的个人交换库公私钥
  16. asp.net nancy_如何在ASP.Net Core中使用Nancy
  17. 用数组实现一个队列改进版
  18. python+openCV使用SIFT算法实现印章的总相似度检测
  19. Linux从零学习记录(四)
  20. VUE搭建云音乐播放器(App版本)

热门文章

  1. 【前端】vue运行环境和编辑器VScode安装
  2. 如何将RGBA 4波段栅格转换为RGB 3波段?分别用ArcGIS及Qgis实现
  3. Oracle date数据类型的字段截取年月日
  4. 计算机中统计个数的函数公式,excel统计文本个数的函数(excel怎么计算不同项个数)...
  5. win10系统每次打开软件都会弹出账户控制如何解决
  6. SSL error when connecting to the Jack server. Try ‘jack-diagnose‘ 报错处理
  7. 用nodejs搭建web服务器
  8. mysql 约束基本概念 主键约束 外键约束
  9. JS指定日期倒计时案例
  10. RLE压缩算法C#详细教程