异步化,高并发大杀器

httpClient同步、异步性能对比 阅读详情

聊聊如何让项目异步化的一些事。

1.同步和异步,阻塞和非阻塞

同步和异步,阻塞和非阻塞, 这个几个词已经是老生常谈,当时常常还是有很多同学分不清楚,以为同步肯定就是阻塞,异步肯定就是非阻塞,其他他们不是一回事。

同步和异步关注的是结果消息的通信机制

同步:同步的意思就是调用方需要主动等待结果的返回

异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等。

阻塞和非阻塞主要关注的是等待结果返回调用方的状态

阻塞:是指结果返回之前,当前线程被挂起,不做任何事

非阻塞:是指结果在返回之前,线程可以做一些其他事,不会被挂起。

可以看见同步和异步,阻塞和非阻塞主要关注的点不同,有人会问同步还能非阻塞,异步还能阻塞?当然是可以的,下面为了更好的说明他们的组合之间的意思,用几个简单的例子说明: 1.同步阻塞:同步阻塞基本也是编程中最常见的模型,打个比方你去商店买衣服,你去了之后发现衣服卖完了,那你就在店里面一直等,期间不做任何事(包括看手机),等着商家进货,直到有货为止,这个效率很低。

2.同步非阻塞:同步非阻塞在编程中可以抽象为一个轮询模式,你去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,你可以去其他地方比如奶茶店,买杯水,但是你还是需要时不时的去商店问老板新衣服到了吗。

3.异步阻塞:异步阻塞这个编程里面用的较少,有点类似你写了个线程池,submit然后马上future.get(),这样线程其实还是挂起的。有点像你去商店买衣服,这个时候发现衣服没有了,这个时候你就给老板留给电话,说衣服到了就给我打电话,然后你就守着这个电话,一直等着他响什么事也不做。这样感觉的确有点傻,所以这个模式用得比较少。

4.异步非阻塞:异步非阻塞这也是现在高并发编程的一个核心,也是今天主要讲的一个核心。好比你去商店买衣服,衣服没了,你只需要给老板说这是我的电话,衣服到了就打。然后你就随心所欲的去玩,也不用操心衣服什么时候到,衣服一到,电话一响就可以去买衣服了。

2.同步阻塞 PK 异步非阻塞

上面已经看到了同步阻塞的效率是多么的低,如果使用同步阻塞的方式去买衣服,你有可能一天只能买一件衣服,其他什么事都不能干,如果用异步非阻塞的方式去买,买衣服只是你一天中进行的一个小事。

我们把这个映射到我们代码中,当我们的线程发生一次rpc调用或者http调用,又或者其他的一些耗时的IO调用,发起之后,如果是同步阻塞,我们的这个线程就会被阻塞挂起,直到结果返回,试想一下如果IO调用很频繁那我们的CPU使用率其实是很低很低。正所谓是物尽其用,既然CPU的使用率被IO调用搞得很低,那我们就可以使用异步非阻塞,当发生IO调用时我并不马上关心结果,我只需要把回调函数写入这次IO调用,我这个时候线程可以继续处理新的请求,当IO调用结束结束时,会调用回调函数。而我们的线程始终处于忙碌之中,这样就能做更多的有意义的事了。

这里首先要说明的是,异步化不是万能,异步化并不能缩短你整个链路调用时间长的问题,但是他能极大的提升你的最大qps。一般我们的业务中有两处比较耗时:

cpu: cpu耗时指的是我们的一般的业务处理逻辑,比如一些数据的运算,对象的序列化。这些异步化是不能解决的,得需要靠一些算法的优化,或者一些高性能框架。

iowait: io耗时就像我们上面说的,一般发生在网络调用,文件传输中等等,这个时候线程一般会挂起阻塞。而我们的异步化通常用于解决这部分的问题。

3.哪些可以异步化?

上面说了异步化是用于解决IO阻塞的问题,而我们一般项目中可以使用异步化如下:

servlet异步化,springmvc异步化

rpc调用如(dubbo,thrift),http调用异步化

数据库调用,缓存调用异步化

下面我会从上面几个方面进行异步化的介绍.

4.servlet异步化

对于Java开发程序员来说servlet并不陌生吧,在项目中不论你使用struts2,还是使用的springmvc,本质上都是封装的servlet。但是我们的一般的开发,其实都是使用的同步阻塞模式如下:

上面的模式优点在于编码简单,适合在项目启动初期,访问量较少,或者是CPU运算较多的项目

缺点在于,业务逻辑线程和servlet容器线程是同一个,一般的业务逻辑总得发生点IO,比如查询数据库,比如产生RPC调用,这个时候就会发生阻塞,而我们的servlet容器线程肯定是有限的,当servlet容器线程都被阻塞的时候我们的服务这个时候就会发生拒绝访问,线程不然我当然们可以通过增加机器的一系列手段来解决这个问题,但是俗话说得好靠人不如靠自己,靠别人替我分担请求,还不如我自己搞定。所以在servlet3.0之后支持了异步化,我们采用异步化之后就会变成如下:

如Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点。如果你想拿高薪的,想学习的,想就业前景好的,想跟别人竞争能取得优势的,想进阿里面试但担心面试不过的,你都可以来,q群号为:856443934

在这里我们采用新的线程处理业务逻辑,IO调用的阻塞就不会影响我们的serlvet了,实现异步serlvet的代码也比较简单,如下:

@WebServlet(name = "WorkServlet",urlPatterns = "/work",asyncSupported =true)

public class WorkServlet extends HttpServlet{

   private static final long serialVersionUID = 1L;

   @Override

   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       this.doPost(req, resp);

   }

   @Override

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       //设置ContentType,关闭缓存

       resp.setContentType("text/plain;charset=UTF-8");

       resp.setHeader("Cache-Control","private");

       resp.setHeader("Pragma","no-cache");

       final PrintWriter writer= resp.getWriter();

       writer.println("老师检查作业了");

       writer.flush();

       List<String> zuoyes=new ArrayList<String>();

       for (int i = 0; i < 10; i++) {

           zuoyes.add("zuoye"+i);;

       }

       //开启异步请求

       final AsyncContext ac=req.startAsync();

       doZuoye(ac, zuoyes);

       writer.println("老师布置作业");

       writer.flush();

   }

   private void doZuoye(final AsyncContext ac, final List<String> zuoyes) {

       ac.setTimeout(1*60*60*1000L);

       ac.start(new Runnable() {

           @Override

           public void run() {

               //通过response获得字符输出流

               try {

                   PrintWriter writer=ac.getResponse().getWriter();

                   for (String zuoye:zuoyes) {

                       writer.println("\""+zuoye+"\"请求处理中");

                       Thread.sleep(1*1000L);

                       writer.flush();

                   }

                   ac.complete();

               } catch (Exception e) {

                   e.printStackTrace();

               }

           }

       });

   }

}

实现serlvet的关键在于http采取了长连接,也就是当请求打过来的时候就算有返回也不会关闭,因为可能还会有数据,直到返回关闭指令。 AsyncContext ac=req.startAsync(); 用于获取异步上下文,后续我们通过这个异步上下文进行回调返回数据,有点像我们买衣服的时候,给老板一个电话,而这个上下文也是一个电话,当有衣服到的时候,也就是当有数据准备好的时候就可以打电话发送数据了。 ac.complete(); 用来进行长链接的关闭。

## 4.1springmvc异步化 现在其实很少人来进行serlvet编程,都是直接采用现成的一些框架,比如struts2,springmvc。下面介绍下使用springmvc如何进行异步化:

首先确认你的项目中的Servlet是3.0以上的!!,其次springMVC4.0+

<dependency>

     <groupId>javax.servlet</groupId>

     <artifactId>javax.servlet-api</artifactId>

     <version>3.1.0</version>

     <scope>provided</scope>

   </dependency>

   <dependency>

     <groupId>org.springframework</groupId>

     <artifactId>spring-webmvc</artifactId>

     <version>4.2.3.RELEASE</version>

   </dependency>

web.xml头部声明,必须要3.0,filter和serverlet设置为异步

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

   <filter>

       <filter-name>testFilter</filter-name>

       <filter-class>com.TestFilter</filter-class>

       <async-supported>true</async-supported>

   </filter>

   <servlet>

       <servlet-name>mvc-dispatcher</servlet-name>

       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

       .........

       <async-supported>true</async-supported>

   </servlet>

使用springmvc封装了servlet的AsyncContext,使用起来比较简单。以前我们同步的模式的Controller是返回额ModelAndView,而异步模式直接生成一个defrredResult(支持我们超时扩展)即可保存上下文,下面给出如何和我们HttpClient搭配的简单demo

@RequestMapping(value="/asynctask", method = RequestMethod.GET)

   public DeferredResult<String> asyncTask() throws IOReactorException {

       IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setIoThreadCount(1).build();

       ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);

       PoolingNHttpClientConnectionManager conManager = new PoolingNHttpClientConnectionManager(ioReactor);

       conManager.setMaxTotal(100);

       conManager.setDefaultMaxPerRoute(100);

       CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setConnectionManager(conManager).build();

       // Start the client

       httpclient.start();

       //设置超时时间200ms

       final DeferredResult<String> deferredResult = new DeferredResult<String>(200L);

       deferredResult.onTimeout(new Runnable() {

           @Override

           public void run() {

               System.out.println("异步调用执行超时!thread id is : " + Thread.currentThread().getId());

               deferredResult.setResult("超时了");

           }

       });

       System.out.println("/asynctask 调用!thread id is : " + Thread.currentThread().getId());

       final HttpGet request2 = new HttpGet("http://www.apache.org/");

       httpclient.execute(request2, new FutureCallback<HttpResponse>() {

           public void completed(final HttpResponse response2) {

               System.out.println(request2.getRequestLine() + "->" + response2.getStatusLine());

               deferredResult.setResult(request2.getRequestLine() + "->" + response2.getStatusLine());

           }

           public void failed(final Exception ex) {

               System.out.println(request2.getRequestLine() + "->" + ex);

           }

           public void cancelled() {

               System.out.println(request2.getRequestLine() + " cancelled");

           }

       });

       return deferredResult;

   }

注意: 在serlvet异步化中有个问题是filter的后置结果处理,没法使用,对于我们一些打点,结果统计直接使用serlvet异步是没法用的。在springmvc中就很好的解决了这个问题,springmvc采用了一个比较取巧的方式通过请求转发,能让请求再次过滤器。但是又引入了新的一个问题那就是过滤器会处理两次,这里可以通过SpringMVC源码中自身判断的方法,我们可以在filter中使用下面这句话来进行判断是不是属于springmvc转发过来的请求,从而不处理filter的前置事件,只处理后置事件:

Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE);

return asyncManagerAttr instanceof WebAsyncManager ;

5.全链路异步化

上面我们介绍了serlvet的异步化,相信细心的同学都看出来似乎并没有解决根本的问题,我的IO阻塞依然存在,只是换了个位置而已,当IO调用频繁同样会让业务线程池快速变满,虽然serlvet容器线程不被阻塞,但是这个业务依然会变得不可用。

那么怎么才能解决上面的问题呢?答案就是全链路异步化,全链路异步追求的是没有阻塞,打满你的CPU,把机器的性能压榨到极致模型图如下:

具体的NIO client到底做了什么事呢,具体如下面模型:

上面就是我们全链路异步的图了(部分线程池可以优化)。全链路的核心在于只要我们遇到IO调用的时候,我们就可以使用NIO,从而避免阻塞,也就解决了之前说的业务线程池被打满得到尴尬场景。

5.1远程调用异步化

我们一般远程调用使用rpc或者http。对于rpc来说一般thrift,http,motan等支持都异步调用,其内部原理也都是采用事件驱动的NIO模型,对于http来说一般的apachehttpclient和okhttp也都提供了异步调用。 下面简单介绍下Http异步化调用是怎么做的: 首先来看一个例子:

public class HTTPAsyncClientDemo {

   public static void main(String[] args) throws ExecutionException, InterruptedException, IOReactorException {

     //具体参数含义下文会讲

      //apache提供了ioReactor的参数配置,这里我们配置IO 线程为1

       IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setIoThreadCount(1).build();

     //根据这个配置创建一个ioReactor

       ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);

     //asyncHttpClient使用PoolingNHttpClientConnectionManager管理我们客户端连接

       PoolingNHttpClientConnectionManager conManager = new PoolingNHttpClientConnectionManager(ioReactor);

     //设置总共的连接的最大数量

       conManager.setMaxTotal(100);

     //设置每个路由的连接的最大数量

       conManager.setDefaultMaxPerRoute(100);

     //创建一个Client

       CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setConnectionManager(conManager).build();

       // Start the client

       httpclient.start();

       // Execute request

       final HttpGet request1 = new HttpGet("http://www.apache.org/");

       Future<HttpResponse> future = httpclient.execute(request1, null);

       // and wait until a response is received

       HttpResponse response1 = future.get();

       System.out.println(request1.getRequestLine() + "->" + response1.getStatusLine());

       // One most likely would want to use a callback for operation result

       final HttpGet request2 = new HttpGet("http://www.apache.org/");

       httpclient.execute(request2, new FutureCallback<HttpResponse>() {

                       //Complete成功后会回调这个方法

           public void completed(final HttpResponse response2) {

               System.out.println(request2.getRequestLine() + "->" + response2.getStatusLine());

           }

           public void failed(final Exception ex) {

               System.out.println(request2.getRequestLine() + "->" + ex);

           }

           public void cancelled() {

               System.out.println(request2.getRequestLine() + " cancelled");

           }

       });

   }

}

下面给出httpAsync的整个类图:

对于我们的HTTPAysncClient 其实最后使用的是InternalHttpAsyncClient,在InternalHttpAsyncClient中有个ConnectionManager,这个就是我们管理连接的管理器,而在httpAsync中只有一个实现那就是PoolingNHttpClientConnectionManager,这个连接管理器中有两个我们比较关心的一个是Reactor,一个是Cpool。

Reactor :所有的Reactor这里都是实现了IOReactor接口。在PoolingNHttpClientConnectionManager中会有拥有一个Reactor,那就是DefaultConnectingIOReactor,这个DefaultConnectingIOReactor,负责处理Acceptor。在DefaultConnectingIOReactor有个excutor方法,生成IOReactor也就是我们图中的BaseIOReactor,进行IO的操作。这个模型就是我们上面的1.2.2的模型

CPool :在PoolingNHttpClientConnectionManager中有个CPool,主要是负责控制我们连接,我们上面所说的maxTotal和defaultMaxPerRoute,都是由其进行控制,如果每个路由的满了他会断开最老的一个链接,如果总共的total满了他会放入leased队列,释放空间的时候就会将其重新连接。

5.2数据库调用异步化

对于数据库调用一般的框架并没有提供异步化的方法,这里推荐自己封装或者使用网上开源的,这里我们公司有个开源的 https://github.com/ainilife/zebra-dao/blob/master/README_ZH.md 能很好的支持异步化

6.最后

异步化并不是高并发的银弹,但是有了异步化的确能提高你机器的qps,吞吐量等等。上述讲的一些模型如果能合理的做一些优化,然后进行应用,相信能对你的服务有很大的帮助的。

从D3 00到FF FF:手把手解析RTCM3.2协议帧结构(附Python解析代码) 本文深入解析RTCM3.2协议帧结构,从同步头标识到CRC校验算法,详细讲解如何通过Python代码实现二进制数据解析。文章包含完整的RTCM3.2解析器实现,涵盖消息类型解码、CRC校验等关键步骤,并附赠特定消息类型(如1005型基准站坐标)的解析示例,帮助开发者快速掌握GNSS差分数据传输协议的核心技术。 阅读详情

相关推荐

PRIDE PPP-AR软件实战:从ZTD解算到PWV反演的全流程避坑指南

本文详细解析了使用PRIDE PPP-AR软件进行GNSS数据处理的全流程,重点涵盖从ZTD解算到PWV反演的关键步骤与精度评估。文章深入剖析了软件部署、数据准备、参数配置中的常见“坑点”,并提供了与IGS ZTD及ERA5产品进行交叉验证的实用方法,旨在帮助用户高效、准确地获取高精度大气可降水量产品。

p8q9r0的博客 606

IO内存以及Reactor模型(更新完了)

同步IO 对文件设备数据的读写需要同步等待操作系统内核,即使文件设备并没有数据可读,线程也会被阻塞住,虽然阻塞时不占用CPU始终周期,但是若需要支持并发连接,则必须启用大量的线程,即每个连接一个线程。这样必不可少的会造成线程大量的上下文切换,随着并发量的增高,性能越来越差。 select模型/poll模型 通过一个线程不断的判断文件句柄数组是否有准备就绪的文件设备,这样就不需要每个线程同步等待,减少了大量线程,降低了线程上下文切换带来的性能损失,提高了线程利用率。这种方式也称为I/O多路复用技术。 sele

Nuan_Feng的博客 842

不用写代码!用AIStarter 5.0.0离线包部署InfiniteTalk的完整指南(Windows/Mac/Linux全平台)

本文详细介绍了如何使用AIStarter 5.0.0离线包在Windows、Mac和Linux平台上零代码部署InfiniteTalk数字人。从环境准备、离线资源获取与验证,到全平台部署实操指南,提供了完整的本地化安装流程和优化建议,帮助用户快速实现AI项目的一键部署。

weixin_30275415的博客 273

Elasticsearch Client 进阶使用

Elasticsearch client的一些进阶用法,以及常见问题。

以梦为马 1478

线上专利申请(超详细)| 签名确认

在【法人登录】页面点击【证书登录】按钮,弹出证书登录提示框,点击【确 认】按钮,进入到输入证书密码页面,点击【取消】按钮,取消本次操作。企业在提交申请时,需要增加签名,如使用企业账户点击提交,会弹出“法人、代理机构不能签名,请使用经办人签名”,这个时候就需要换个账号了。在需要签名时,将专利官网的登录方式点击法人登录,选择扫码登录,使用移动端经办人账户的扫一扫功能扫描登录。登录完成后,再点击签名,使用移动端扫一扫功能,扫描签名,输入证书号,确认签名,并提交。最后,祝大家的申请都成功!

weixin_62112323的博客 9111

ArcGIS实战:用栅格数据为偏远山区规划一条最省钱的公路(附完整数据与操作步骤)

本文详细介绍了如何利用ArcGIS进行栅格数据分析,为偏远山区规划一条最省钱的公路。通过DEM高程数据处理、成本权重设定和最佳路径计算,结合真实案例展示了从数据准备到工程方案转化的完整流程,帮助工程师在复杂地形中做出经济高效的路线决策。

weixin_32136203的博客 357

高并发的“大杀器”:异步化、并行化

高并发的大杀器:异步化同步和异步,阻塞和非阻塞同步和异步,阻塞和非阻塞,这几个词已经是老生常谈,但是还是有很多同学分不清楚,以为同步肯定就是阻塞,异步肯定就是非阻塞,其实他们并不是一回事。同步和异步关注的是结果消息的通信机制:同步:调用方需要主动等待结果的返回。异步:不需要主动等待结果的返回,而是通过其他手段,比如状态通知,回调函数等。阻塞和非阻塞主要关注...

xingduan5153的博客 722

网页内容抓取的艺术:使用R语言解析动态网页

本文探讨了使用R语言处理动态网页数据的方法。针对AJAX加载的学术网站,传统的静态抓取和RSelenium方法存在会话终止、浏览器错误等问题。通过分析发现内容通过admin-ajax.php加载,提出使用httr2模拟POST请求获取数据,配合rvest解析HTML内容。该方法避免了浏览器自动化的弊端,实现了高效稳定的动态内容提取,为处理类似AJAX网站提供了通用解决方案。

m0_74337424的博客 54

Elasticsearch学习笔记

文章目录资料收集基础入门Elasticsearch概念与MySQL对应MySQL同步ES几种同步方案数据一致性问题主流解决方案其他Kibana 资料收集 公司组件文档 公司ES组件介绍:包含最佳实践案例、SDK API介绍、多机房容灾部署、ES平台架构等,协助公司开发迅速使用。 ES官方权威指南文档 英文最新版权威指南 ES入门教程 基础入门 主要学习资料:ES入门教程 全部是最基本的操作,有个感觉。 一、安装教程 二、基本概念 注意点: Elastic 会索引所有字段,经过处理后写入一个反向索引(Inv

weixin_38370441的博客 1018

适航文章汇总:知识体系全面梳理

无论您是行业新手还是经验丰富的专业人士,都可以通过本篇文章快速查找相关资料,提高对适航认证、机载系统、软件及硬件等领域的理解。本部分详细解析ARP 4754A、ARP 4761、DO-178C、DO-254等核心标准,并介绍其在机载系统开发中的应用。介绍FAA、EASA、CCAR等主要航空管理机构的适航规章体系,并说明各类飞行器(如固定翼、旋翼机、无人机等)的适航要求。本部分还探讨硬件设计、验证与确认过程中的挑战。从系统架构设计到机载设备的集成与管理,本部分介绍机载系统的研发体系及适航管理的最佳实践。

qq_41854911的博客 1451

Level 4自动驾驶系统设计49——L4 架构设计 2

在 9.3.2 节所述的 SerDes 晶圆级原生“双出(Splitter Mode)”物理架构中,若解串芯片内部的其中一路 MIPI 供电轨因大算力 SoC 1 的高热漏电流污染突发二阶瞬态电磁耦合,导致双出通道产生 $\mathbf{\Delta} \text{Skew} \ge 15\text{ps}$ 的时序偏置。其中,$\mathbf{H}(t)$ 为最低限度生存网雷达几何测量的观测矩阵,$\mathbf{R}_{\text{fallback}}$ 为清洗后的雷达测量不确定度协方差。

Godspeed_zwh的博客 309

异步化,你的高并发大杀器

今天来聊聊如何让项目异步化的一些事。 1.同步和异步,阻塞和非阻塞 同步和异步,阻塞和非阻塞, 这个几个词已经是老生常谈,当时常常还是有很多同学分不清楚,以为同步肯定就是阻塞,异步肯定就是非阻塞,其他他们不是一回事。 同步和异步关注的是结果消息的通信机制 - 同步:同步的意思就是调用方需要主动等待结果的返回 - 异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态...

lizhao007 3077

【测开求职】面试题:操作系统 吐血整理

本篇文章是笔者为准备研究生复试和校招而写,因此主要针对问答,注重理解,而不在于细节计算。涵盖考研范围内的所有知识内容,读者可以先查看上面的链接走一遍操作系统的整体框架结构。其中,重点难点以及易混淆的知识点,笔者用其他文章进行了详述,在知识点处贴好了链接,有需要的可以查阅。另外操作系统在校招中有非常明显的重点,一般情况下只会考察进程管理,内存管理,和设备管理,且这三者考察频率依次递减,文件管理基本不会考察。

vector的博客 7908

并行化-你的高并发大杀器

可以想象有这么一个需求,每天定时对id在某个范围之间的用户发券,比如这个范围之间的用户有几百万,如果给一台机器发的话,可能全部发完需要很久的时间,所以分布式调度框架比如:elastic-job都提供了分片的功能,比如你用50台机器,那么id%50=0的在第0台机器上,=1的在第1台机器上发券,那么我们的执行时间其实就分摊到了不同的机器上了。所以多核CPU逐渐成为现在市场的主流,这样让我们的多线程编程也更加的容易。本文介绍了什么是并行化,并行化的各种历史,在Java中如何实现并行化,以及并行化的注意事项。..

m0_63592370的博客 391

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: 大型互联网架构演化简史
下一篇: 阿里Java研发面试题『八部曲』——从第一步开始
Java_supermanNO1
博客等级 码龄7年 1170粉丝 329原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值