分布式服务框架——Dubbo
1. Dubbo 概述
1.1 什么是分布式系统
- 《分布式系统原理与范型》定义:
- “分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
- 分布式系统(distributed system)是建立在网络之上的软件系统
- 简单来说:多个(不同职责)人共同来完成一件事!
- 任何一台服务器都无法满足淘宝的双十一的数据吞吐量,一定是很多台服务器公共来完成的
- 歇后语:“三个臭皮匠赛过诸葛亮”,就是分布式系统的真实写照
1.1.1 单一应用架构
当网站流量很小时,只需要一个应用,将所有的功能部署到一起(所有业务都放在一个 tomcat 里),从而减少部署节点和成本;
此时,用于简化 增删改查 工作量的数据访问框架 (ORM)是关键;
- ORM:对象关系映射(Object Relational Mapping)
例如:某个超市的收银系统,某个公司的员工管理系统
优点
- 小项目开发快 成本低
- 架构简单
- 易于测试
- 易于部署
缺点
- 大项目模块耦合严重,不易开发维护,沟通成本高
- 新增业务困难
- 核心业务与边缘业务混合在一块,出现问题互相影响
1.1.2 垂直应用架构
- 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成几个互不相干的几个应用,以提高效率;
- 大模块按照 mvc 分层模式,进行拆分成多个互不相关的小模块,并且每个小模块都有独立的服务器
- 此时,用于加速前端页面开发的 web 框架(MVC)是关键;因为每个小应用都有独立的页面
- MVC:模型视图控制器 (Model View Controller)
1)缺点
- 模块之间不可能完全没有交集,公用模块无法重复利用,开发性的浪费
1.1.3 分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的业务,逐渐形成稳健的服务中心,使前端应用能更快速的响应多变的市场需求;
此时,用户提高业务复用及整合的分布式服务框架(RPC)远程调用是关键;
RPC:独立的应用服务器之间,要依靠 RPC(Romote Procedure Call)才能调用
问题:物流服务不忙,有100 台服务器; 商品服务特别忙,也是100 台服务器;如何做到资源优化调配?
1.1.4 流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐呈现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率;
此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键;
SOA:面向服务架构(Service-Oriented Architecture),简单理解就是“服务治理”,例如:公交车站的“调度员”
1.2 Dubbo 简介
- Dubbo 是分布式服务框架,是阿里巴巴的开源项目,现交给 apache 进行维护
- Dubbo 致力于提高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案
- 简单来说,Dubbo 是个服务框架,如果没有分布式的需求,是不需要用的
1.2.1 RPC
- RPC(Remote Procedure Call)是指远程过程调用,是一种进程间通信方式
- RPC 基本的通信原理
- 在客户端将对象进行序列化
- 底层通信框架使用 netty(基于 tcp 协议的 socket),将序列化的对象发给服务方提供方
- 服务提供方通过 socket 得到数据文件之后,进行反序列化,获得要操作的对象
- 对象数据操作完毕,将新的对象序列化,再通过服务提供方的 socket 返回给客户端
- 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次请求
- RPC 两个核心模块:通讯(socket),序列化。
1.2.2 节点角色
节点 | 角色说明 |
---|---|
Provider | 服务的提供方 |
Consumer | 服务的消费方 |
Registry | 服务注册与发现的注册中心(便民服务中心,所有的饭店娱乐场所都在已在本中心注册) |
Monitor | 监控服务的统计中心(统计服务被调用的次数) |
Container | 服务运行容器(烧烤一条街,洗浴一条街) |
1.2.3 调用关系
- 服务容器负责启动,加载,运行服务提供者;
- 服务提供者在启动时,向注册中心注册自己提供的服务;
- 服务消费者在启动时,向注册中心订阅自己所需的服务;
- 在注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用;
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心;
2. 快速入门
官网:http://dubbo.apache.org/
2.1 注册中心
2.2.1 Zookeeper
- 官方推荐使用 Zookeeper 注册中心;
- 注册中心负责服务地址的注册与查找,相当于目录服务;
- 服务提供者和消费者只在启动时与注册中心交互,注册中不转发请求,压力较小;
- Zookeeper 是 apache hadoop 的子项目,是一个树形的目录服务,支持变更推送,适合作为 Dubbo 的服务注册中心,工业强度较高,可用于生产环境;
Dubbo 即是求职的人,也是招聘单位,而 Zookeeper 就是人才市场/招聘网站;
2.2 服务提供方
- 一个空的 maven 项目
- 提供一个服务接口即可
2.2.1 服务方的 pom.xml
<packaging>war</packaging>
<properties><spring.version>5.0.6.RELEASE</spring.version>
</properties>
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><!--dubbo --><dependency><groupId>com.alibaba</groupId><artifactId>dubbo</artifactId><version>2.5.7</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.6</version></dependency><dependency><groupId>com.github.sgroschupf</groupId><artifactId>zkclient</artifactId><version>0.1</version></dependency><dependency><groupId>javassist</groupId><artifactId>javassist</artifactId><version>3.11.0.GA</version></dependency>
</dependencies>
<build><plugins><plugin><!--tomcat 内嵌到web项目中--><groupId>org.apache.tomcat.maven </groupId><artifactId>tomcat7-maven-plugin</artifactId><configuration><port>8001</port><path>/</path></configuration><executions><execution><!-- 打包完成后,运行服务 --><phase>package</phase><goals><goal>run</goal></goals></execution></executions></plugin></plugins>
</build>
2.2.2 服务方接口
public interface HelloService {String sayHello(String name);
}
2.2.3 服务方实现
@com.alibaba.dubbo.config.annotation.Service
public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String name) {return "Hello," + name + "!!!";}
}
2.2.4 服务方的配置文件 spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!--1.服务提供方在zookeeper中的“别名”--><dubbo:application name="dubbo-server"/><!--2.注册中心的地址--><dubbo:registry address="zookeeper://192.168.204.141:2181"/><!--3.扫描类(将什么包下的类作为服务提供类)--><dubbo:annotation package="service.impl"/>
</beans>
2.2.5 服务方的 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"id="WebApp_ID" version="3.1"><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/spring.xml</param-value></context-param>
</web-app>
2.3 服务消费方
2.3.1 消费方的 pom.xml
与服务方一致,只需要修改 tomcat 的端口为 8002
2.3.2 消费方的 Controller
@RestController
public class HelloAction {@com.alibaba.dubbo.config.annotation.Referenceprivate HelloService hs;@RequestMapping("hello")@ResponseBodypublic String hello( String name){return hs.sayHello(name);}
}
2.3.3 消费方的接口
注意:controller 中要依赖 HelloService,所以我们创建一个接口;这里是消费方,不需要实现,因为实现会让服务方为我们搞定!
public interface HelloService {String sayHello(String name);
}
2.3.4 消费方的 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"id="WebApp_ID" version="3.1"><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/spring.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
2.3.5 消费方的 springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!--Dubbo的应用名称,通常使用项目名 --><dubbo:application name="dubbo-consumer" /><!--配置Dubbo的注册中心地址 --><dubbo:registry address="zookeeper://192.168.204.141:2181" /><!--配置Dubbo扫描类,将这个类作为服务进行发布 --><dubbo:annotation package="controller" />
</beans>
2.4 启动服务测试
首先启动服务方,再启动消费方。
访问:http://localhost:8002/hello?name=james
3. 综合实战
https://dubbo.apache.org/zh/docs/v2.7/user/examples/
3.1 配置说明
3.1.1 启动时检查
- 启动时会在注册中心检查依赖的服务是否可用,不可用时会抛出异常
- 在消费方编写初始化容器的 main 方法启动(tomcat 启动方式,必须访问一次 action 才能初始化spring)
public class Test {public static void main(String[] args) throws IOException {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring.xml");System.in.read();}
}
<!--默认是true:抛异常;false:不抛异常-->
<dubbo:consumer check="false" />
- 系统级别日志,需要配合 log4j 才输出,在 resources 下添加 log4j.properties,内容如下:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=dubbo.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
log4j.rootLogger=error, stdout,file
3.1.2 超时时间
- 由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
- 为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
- 在服务提供者添加如下配置:
<!--设置超时时间为2秒,默认为1秒-->
<dubbo:provider timeout="2000"/>
- 可以将服务实现 HelloServiceImpl.java 中加入模拟的网络延迟进行测试:
@Service
public class HelloServiceImpl implements HelloService {public String sayHello(String name) {try {Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}return "Hello,"+name+"!!!!!";}
}
- 超时设置 2 秒,而模拟的网络延迟有 3 秒,超出时限,报错!
- 配置原则:
- Dubbo 推荐在 Provider 上尽量多配置 Consumer 端属性:
- 作为服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
- 在 Provider 配置后,Consumer 不配置则会使用 Provider 的配置值,即 Provider 配置可以作消费者的缺省值。
- Dubbo 推荐在 Provider 上尽量多配置 Consumer 端属性:
3.1.3 重试次数
- 当出现失败,自动切换并重试其它服务器,Dubbo 重试的缺省值是 2 次,我们可以自行设置
- 在 Provider 提供方配置:
<!-- 消费方连接第1次不算,再来重试3次,总共重试4次 -->
<dubbo:provider timeout="2000" retries="3"/>
@Service
public class HelloServiceImpl implements HelloService {public String sayHello(String name) {System.out.println("=============被调用 1 次===============");try {Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}return "Hello,"+name+"!!!!!";}
}
并不是所有的方法都适合设置重试次数
- 幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
- 非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
单独设置某个方法
提供方接口添加 sayNo() 方法并实现
public interface HelloService {String sayHello( String name );String sayNo(); }@Service public class HelloServiceImpl implements HelloService {public String sayNo() {System.out.println("-------no被调用一次-------");return "no!";} }
消费方接口添加 sayNo() 方法声明
public interface HelloService {String sayHello( String name );String sayNo();}
消费方 controller
@Controller public class HelloAction {//@Reference 此注解已经在xml文件中被<dubbo:reference>顶替,所以自动注入即可@Autowiredprivate HelloService helloService;@GetMapping("hello")@ResponseBodypublic String sayHi(String name){return helloService.sayHello(name);}@GetMapping("no")@ResponseBodypublic String no(){ return helloService.sayNo();} }
消费方配置方法重试次数
<dubbo:reference interface="service.HelloService" id="helloService"><dubbo:method name="sayHello" retries="3"/><dubbo:method name="sayNo" retries="0"/> <!-- 不重试 --> </dubbo:reference>
3.1.4 多版本
- 一个接口,多个(版本的)实现类,可以使用定义版本的方式引入
- 为 HelloService 接口定义两个实现类,提供者修改配置:
<dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl01" version="1.0.0"/><dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl02" version="2.0.0"/>
- 消费者就可以根据 version 的版本,选择具体的服务版本
<dubbo:reference interface="service.HelloService" id="helloService" version="2.0.0"><dubbo:method name="sayHello" retries="3"/><dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
- 注意:消费者的控制层要改为自动注入,因为 @Reference 注解和
<dubbo:reference/>
在这里冲突
@Controller
public class HelloAction {@Autowiredprivate HelloService helloService;
}
- 当消费者的版本修改为 version="*",那么就会随机调用服务提供者的版本
3.1.5 本地存根
- 目前我们的分布式架构搭建起来有一个严重的问题,就是所有的操作全都是 消费者发起,由服务提供者执行
- 消费者动动嘴皮子却什么活都不干,这样会让提供者很累,例如简单的参数验证,消费者完全能够胜任,把合法的参数再发送给提供者执行,效率高了,提供者也没那么累了
- 先在消费者处理一些业务逻辑,再调用提供者的过程,就是“本地存根”
- 代码实现肯定在 消费者,创建一个 HelloServiceStub 类并且实现 HelloService 接口
- 注意:必须使用构造方法的方式注入
public class HelloServiceStub implements HelloService {private HelloService helloService;// 注入HelloServicepublic HelloServiceStub(HelloService helloService) {this.helloService = helloService;}public String sayHello(String name) {System.out.println("本地存根数据验证。。。");if(!StringUtils.isEmpty(name)){return helloService.sayHello(name);}return "i am sorry!";}public String sayNo() {return helloService.sayNo();}
}
- 修改消费者配置:
<dubbo:reference interface="service.HelloService" id="helloService" version="1.0.0" stub="service.impl.HelloServiceStub"><dubbo:method name="sayHello" retries="3"/><dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
3.2 负载均衡策略
负载均衡(Load Balance), 其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务
Dubbo一共提供 4 种策略,缺省为 random 随机分配调用
修改提供者配置并启动 3 个提供者,让消费者对其进行访问
tomcat 端口 8001,8002,8003(每次启动前修改,可以开多个窗口,即多个提供者)
provider 端口 20881,20882,20883(每次启动前修改,可以开多个窗口,即多个消费者)
HelloServiceImpl01 类,服务器1,服务器2,服务器3
启动 consumer 进行测试
消费方修改权重
最好使用管理端修改权重
3.3 高可用(Zookeeper 宕机)
- Zookeeper 注册中心宕机,还可以消费 Dubbo 暴露的服务
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
- 测试:
- 正常发出请求
- 关闭 Zookeeper:
./zkServer.sh stop
- 消费者仍然可以正常消费
3.4 服务降级
- 壁虎遇到危险会自动脱落尾巴,目的是损失不重要的东西,保住重要的
- 服务降级,就是根据实际的情况和流量,对一些服务有策略的停止或换种简单的方式处理,从而释放服务器的资源来保证核心业务的正常运行
3.4.1 为什么要服务降级
- 为什么要使用服务降级,这是防止分布式服务发生雪崩效应
- 什么是雪崩?就是蝴蝶效应,当一个请求发生超时,一直等待着服务响应,那么在高并发情况下,很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,而宕机之后会导致分布式其他服务调用该宕机的服务也会出现资源耗尽宕机,这样下去将导致整个分布式服务都瘫痪,这就是雪崩
3.4.2 服务降级实现方式
在管理控制台配置服务降级:屏蔽和容错
屏蔽:
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。容错:
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
分布式服务框架——Dubbo相关推荐
- 11月15日云栖精选夜读:分布式服务框架Dubbo疯狂更新!阿里开源要搞大事情?
最近,开源社区发生了一件大事--使用最广的开源服务框架之一Dubbo低调重启维护,并且3个月连续发布了3个维护版本.这3个维护版本不仅解决了社区关心的一系列问题和需求,还让整个社区的活跃度得到了大幅提 ...
- 阿里分布式服务框架Dubbo的架构总结
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合).从服务模型的角度来看,Dubbo采用的是一种非常简单的模 ...
- 阿里巴巴分布式服务框架 Dubbo 团队成员梁飞专访
专家访谈 目录(?)[-] 先来个自我介绍吧! Dubbo是什么?能做什么? Dubbo适用于哪些场景? Dubbo的设计思路是什么? Dubbo的需求和依赖情况? Dubbo的性能如何? 和淘宝HS ...
- 分布式服务框架Dubbo使用小结
介绍: Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA 服务治理方案,每天为2,000+个服务提供3,000,000,0 ...
- java分布式服务框架Dubbo的介绍与使用
1. Dubbo是什么? Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案.简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需 ...
- (转)阿里巴巴分布式服务框架 Dubbo 团队成员梁飞专访
Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,每天为2000+ 个服务提供3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点.Dubbo自2011年开源后, ...
- 团队成员梁飞专访:阿里巴巴分布式服务框架Dubbo
Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,每天为2000+ 个服务提供3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点.Dubbo自2011年开源后, ...
- 分布式服务框架 dubbo/dubbox 入门示例(转)
dubbo是一个分布式的服务架构,可直接用于生产环境作为SOA服务框架. 官网首页:http://dubbo.io/ ,官方用户指南 http://dubbo.io/User+Guide-zh.htm ...
- 分布式服务框架Dubbo疯狂更新!阿里开源要搞大事情?
最近,开源社区发生了一件大事--使用最广的开源服务框架之一Dubbo低调重启维护,并且3个月连续发布了3个维护版本.这3个维护版本不仅解决了社区关心的一系列问题和需求,还让整个社区的活跃度得到了大幅提 ...
最新文章
- xgboost重要参数2为主但不全要参照1
- Python 基础 - Day 2 Assignment - ShoppingCart 购物车程序
- Linux下使用nmap扫描局域网存活的IP
- Flow - JS静态类型检查工具
- ASP.NET Core 实现带认证功能的Web代理服务器
- 计算机视觉应用的简称,计算机视觉应用之图像检索任务简单介绍
- 自动机器学习(AutoML)最新综述
- C 语言精髓之变参函数
- WordPress主题-RiPro子主题|小八子主题v8.0版
- 域名升级访问中拿笔记好_SEO优化中如何让你的页面访问速度更快
- php rewrite
- c# implicit explicit关键字(隐式和显式数据类型转换)
- Himall商城文件帮助类IOHelper(2)
- 计算机网络知识点汇总(谢希仁 第七版)
- cornell movie-dialogs corpus 康奈尔大学电影对话语料介绍及下载 可用于dialog,chatbot
- 国际贸易基础(三)商检
- ffmpeg过滤器系列:序章,什么是ffmpeg过滤器?ffmpeg过滤器可以用来做什么
- 北京科技大学计算机考研
- PS图层模式详细讲解
- python聊天智能回复_【微信】设置自动回复消息和智能聊天
热门文章
- 爬虫被锁IP,报“很抱歉,由于您访问的URL有可能对网站造成安全威胁,您的访问被阻断。 您的请求ID是······“
- Go Http 错误记录
- 全场景覆盖的华为云游戏文娱解决方案
- 2013华为上机题C++编程
- 简要说明html5工作原理,简要说明PLC的工作原理?
- 信号系统 | 信号的概念与常用信号
- 极客学院内部首发唯一完整实战微信小程序培训课程,现在免费赠送(价值500
- 西二旗地铁换乘绕行指南
- 哈佛MBA精粹42条
- 遥感辐射定律中用到的常数的精确值