【云原生 | Kubernetes篇】Kubernetes简介
Kubernetes简介一、背景1、部署方式的变迁传统部署时代:在物理服务器上运行应用程序无法为应用程序定义资源边界导致资源分配问题例如,如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况, 结果可能导致其他应用程序的性能下降。 一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展, 并且维护许多物理服务器的成本很高。虚拟化部署时代:作为解决方案,引入了虚拟化虚拟化技术允许你在单个物理服务器的 CPU 上运行多个虚拟机(VM)虚拟化允许应用程序在 VM 之间隔离,并提供一定程度的安全一个应用程序的信息 不能被另一应用程序随意访问。虚拟化技术能够更好地利用物理服务器上的资源因为可轻松地添加或更新应用程序 ,所以可以实现更好的可伸缩性,降低硬件成本等等。每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。缺点:虚拟层冗余导致的资源浪费与性能下降容器部署时代:容器类似于 VM,但可以在应用程序之间共享操作系统(OS)。容器被认为是轻量级的。容器与 VM 类似,具有自己的文件系统、CPU、内存、进程空间等。由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。容器优势:敏捷性:敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。及时性:持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性),支持可靠且频繁的 容器镜像构建和部署。解耦性:关注开发与运维的分离:在构建/发布时创建应用程序容器镜像,而不是在部署时。 从而将应用程序与基础架构分离。可观测性:可观察性不仅可以显示操作系统级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。跨平台:跨开发、测试和生产的环境一致性:在便携式计算机上与在云中相同地运行。可移植:跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。简易性:以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。大分布式:松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。隔离性:资源隔离:可预测的应用程序性能。高效性:资源利用:高效率和高密度2、容器化问题弹性的容器化应用管理强大的故障转移能力高性能的负载均衡访问机制便捷的扩展自动化的资源监测3、为什么用 Kubernetes容器是打包和运行应用程序的好方式。在生产环境中,你需要管理运行应用程序的容器,并确保不会停机。 例如,如果一个容器发生故障,则需要启动另一个容器。如果系统处理此行为,会不会更容易?这就是 Kubernetes 来解决这些问题的方法! Kubernetes 为你提供了一个可弹性运行分布式系统的框架。linux之上的一个服务编排框架;Kubernetes 会满足你的扩展要求、故障转移、部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary 部署。Kubernetes 为你提供:服务发现和负载均衡Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。存储编排Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。自动部署和回滚你可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态 更改为期望状态。例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。自动完成装箱计算Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。 当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。自我修复Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的 运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。密钥与配置管理Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥为了生产环境的容器化大规模应用编排,必须有一个自动化的框架。4、市场份额4.1、容器化docker swarm?4.2、服务编排?google --- kubernetes --- 发起cncf --- 众多的项目辅佐 kubernetes ---- kubernetes +cncf其他软件 = 整个大型云平台二、简介Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。名称 Kubernetes 源于希腊语,意为“舵手”或“飞行员”。Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在Google 在大规模运行生产工作负载方面拥有十几年的经验 的基础上,结合了社区中最好的想法和实践。1、Kubernetes不是什么Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。Kubernetes 在容器级别而不是在硬件级别运行它提供了 PaaS 产品共有的一些普遍适用的功能, 例如部署、扩展、负载均衡、日志记录和监视。但是,Kubernetes 不是单体系统,默认解决方案都是可选和可插拔的。 Kubernetes 提供了构建开发人员平台的基础,但是在重要的地方保留了用户的选择和灵活性。Kubernetes:不限制支持的应用程序类型。 Kubernetes 旨在支持极其多种多样的工作负载,包括无状态、有状态和数据处理工作负载。 如果应用程序可以在容器中运行,那么它应该可以在 Kubernetes 上很好地运行。不部署源代码,也不构建你的应用程序。 持续集成(CI)、交付和部署(CI/CD)工作流取决于组织的文化和偏好以及技术要求。不提供应用程序级别的服务作为内置服务,例如中间件(例如,消息中间件)、 数据处理框架(例如,Spark)、数据库(例如,mysql)、缓存、集群存储系统 (例如,Ceph)。这样的组件可以在 Kubernetes 上运行,并且/或者可以由运行在 Kubernetes 上的应用程序通过可移植机制(例如, 开放服务代理)来访问。不要求日志记录、监视或警报解决方案。 它提供了一些集成作为概念证明,并提供了收集和导出指标的机制。不提供或不要求配置语言/系统(例如 jsonnet),它提供了声明性 API, 该声明性 API 可以由任意形式的声明性规范所构成。RESTful;写yaml文件不提供也不采用任何全面的机器配置、维护、管理或自我修复系统。此外,Kubernetes 不仅仅是一个编排系统,实际上它消除了编排的需要。 编排的技术定义是执行已定义的工作流程:首先执行 A,然后执行 B,再执行 C。 相比之下,Kubernetes 包含一组独立的、可组合的控制过程, 这些过程连续地将当前状态驱动到所提供的所需状态。 如何从 A 到 C 的方式无关紧要,也不需要集中控制,这使得系统更易于使用 且功能更强大、系统更健壮、更为弹性和可扩展。
eureka技术分享
一、Eureka的基础知识Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。上图简要描述了Eureka的基本架构,由3个角色组成:1.Eureka Server 提供服务注册和发现2.Service Provider 服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到3.Service Consumer 服务消费方从Eureka获取注册服务列表,从而能够消费服务二、Eureka的交互流程与原理图是来自Eureka官方的架构图,大致描述了Eureka集群的工作过程。图中包含的组件非常多,可能比较难以理解,我们用通俗易懂的语言解释一下:Application Service 相当于本书中的服务提供者,Application Client相当于服务消费者;Make Remote Call,可以简单理解为调用RESTful API;us-east-1c、us-east-1d等都是zone,它们都属于us-east-1这个region;由图可知,Eureka包含两个组件:Eureka Server 和 Eureka Client,它们的作用如下:?Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒);每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步;Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。综上,Eureka通过心跳检测、健康检查和客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。?三、搭建Eureka注册中心1、搭建Eureka服务中心(1) 创建shop_eureka_server子模块在 shop_parent 下创建子模块 shop_eureka_server(2) 引入maven坐标<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>(3) 配置application.ymlserver:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ registerWithEureka: 是否将自己注册到Eureka服务中,本身就是所有无需注册
fetchRegistry : 是否从Eureka中获取注册信息
serviceUrlEureka: 客户端与Eureka服务端进行交互的地址(4) 配置启动类在 cn.itcast.eureka 下创建启动类 EurekaServerApplication@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}EnableEurekaServer : 激活Eureka Server端配置2、服务注册中心管理后台打开浏览器访问http://localhost8761即可进入EurekaServer内置的管理控制台,显示效果如下四、服务注册到Eureka注册中心1、商品服务注册(1) 商品模块中引入坐标在 shop_service_product 的pom文件中添加eureka client的相关坐标<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>(2) 配置application.yml文件在工程的 application.yml 中添加Eureka Server的主机地址eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true #使用ip注册(3) 修改启动类添加服务注册注解?@SpringBootApplication
//@EnableDiscoveryClient
//@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient 可省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。2、订单服务注册和商品微服务一样,只需要引入坐标依赖,在工程的 application.yml 中添加Eureka Server的主机地址即可(1) 订单模块中引入坐标在 shop_service_product 的pom文件中添加eureka client的相关坐标<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
(2) 配置application.yml文件在工程的 application.yml 中添加Eureka Server的主机地址eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true #使用ip注册(3) 修改启动类添加服务注册注解@SpringBootApplication
//@EnableDiscoveryClient
//@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient 可省略。只需加上相关依赖,并进行相应配置,即可将微服务注册到服务发现组件上。五、Eureka中的自我保护微服务第一次注册成功之后,每30秒会发送一次心跳将服务的实例信息注册到注册中心。通知 Eureka Server 该实例仍然存在。如果超过90秒没有发送更新,则服务器将从注册信息中将此服务移除。Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)验证完自我保护机制开启后,并不会马上呈现到web上,而是默认需等待 5 分钟(可以通eureka.server.wait-time-in-ms-when-sync-empty 配置),即 5 分钟后你会看到下面的提示信息:如果关闭自我保护通过设置 eureka.enableSelfPreservation=false 来关闭自我保护功能。六、Eureka中的元数据Eureka的元数据有两种:标准元数据和自定义元数据。标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问。在程序中可以使用DiscoveryClient 获取指定微服务的所有元数据信息@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class RestTemplateTest {
@Autowired
private DiscoveryClient discoveryClient;
@Test
public void test() {
//根据微服务名称从注册中心获取相关的元数据信息
List<ServiceInstance> instances = discoveryClient.getInstances("shop-service-product");
for (ServiceInstance instance : instances) {
System.out.println(instance);
}
}
}七、Eureka高可用集群Eureka Client会定时连接Eureka Server,获取注册表中的信息并缓存到本地。微服务在消费远程API时总是使用本地缓存中的数据。因此一般来说,即使Eureka Server发生宕机,也不会影响到服务之间的调用。但如果Eureka Server宕机时,某些微服务也出现了不可用的情况,Eureka Server中的缓存若不被刷新,就可能会影响到微服务的调用,甚至影响到整个应用系统的高可用。因此,在生成环境中,通常会部署一个高可用的Eureka Server集群。Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server实例会彼此增量地同步信息,从而确保所有节点数据一致。事实上,节点之间相互注册是Eureka Server的默认行为。1、搭建 Eureka Server高可用集群1、修改本机host属性127.0.0.1 eureka1127.0.0.1 eureka22、修改 shop_eureka_server 工程中的yml配置文件,添加如下配置属性?#指定应用名称
spring:
application:
name: shop-eureka-server
---
#执行peer1的配置信息
spring:
profiles: eureka1
server:
port: 8761
eureka:
instance:
hostname: eureka1
client:
service-url:
defaultZone: http://eureka2:8762/eureka
---
#执行peer2的配置信息
spring:
profiles: eureka2
server:
port: 8762
eureka:
instance:
hostname: eureka2
client:
service-url:
defaultZone: http://eureka1:8761/eureka在配置文件中通过连字符(—)将文件分为三个部分,第一部分为应用名称,第二部分和第三部分是根据不同的profiles选项动态添加,可以在IDEA启动时进行激活配置使用IDEA启动历次EurekaServerApplicaion分别激活eureka1和eureka2配置。访问http://eureka1:8761和http://eureka1:8762/。会发现注册中心 SHOP-EUREKA-SERVER 已经有两个节点,并且registered-replicas (相邻集群复制节点)中已经包含对方。2、服务注册到Eureka Server集群如果需要将微服务注册到Eureka Server集群只需要修改yml配置文件即可eureka:
client:
serviceUrl:
defaultZone: http://eureka1:8761/eureka/,http://eureka1:8761/eureka/以商品微服务为例,修改defaultZone配置添加多个Eureka Server的地址八、Eureka中的常见问题1、服务注册慢默认情况下,服务注册到Eureka Server的过程较慢。SpringCloud官方文档中给出了详细的原因大致含义:服务的注册涉及到心跳,默认心跳间隔为30s。在实例、服务器、客户端都在本地缓存中具有相同的元数据之前,服务不可用于客户端发现(所以可能需要3次心跳)。可以通过配置eureka.instance.leaseRenewalIntervalInSeconds (心跳频率)加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为在服务器内部有一些计算,他们对续约做出假设。2、服务节点剔除问题默认情况下,由于Eureka Server剔除失效服务间隔时间为90s且存在自我保护的机制。所以不能有效而迅速的剔除失效节点,这对开发或测试会造成困扰。解决方案如下:Eureka Server:配置关闭自我保护,设置剔除无效节点的时间间隔eureka:
instance:
hostname: eureka1
client:
service-url:
defaultZone: http://eureka2:8762/eureka
server:
enable-self-preservation: false #关闭自我保护
eviction-interval-timer-in-ms: 4000 #剔除时间间隔,单位:毫秒Eureka Client:配置开启健康检查,并设置续约时间eureka:
client:
healthcheck: true #开启健康检查(依赖spring-boot-actuator)
serviceUrl:
defaultZone: http://eureka1:8761/eureka/,http://eureka1:8761/eureka/
instance:
preferIpAddress: true
lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给server端后,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约间隔3、监控页面显示ip在Eureka Server的管控台中,显示的服务实例名称默认情况下是微服务定义的名称和端口。为了更好的对所有服务进行定位,微服务注册到Eureka Server的时候可以手动配置示例ID。配置方式如下:eureka:
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
#spring.cloud.client.ip-address:获取ip地址九、Eureka配置合集1、Eureka Server常用配置eureka:
server:
#服务端开启自我保护模式
enable-self-preservation: true
#扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒
eviction-interval-timer-in-ms: 60000
#间隔多长时间,清除过期的 delta 数据
delta-retention-timer-interval-in-ms: 0
#是否开启请求频率限制器
rate-limiter-enabled: false
#请求频率限制器
rate-limiter-burst-size: 10
#请求频率的平均值
rate-limiter-full-fetch-average-rate: 100
#是否对标准的client进行频率请求限制。如果是false,则只对非标准client进行限制
rate-limiter-throttle-standard-clients: false
#注册服务、拉取服务列表数据的请求频率的平均值
rate-limiter-registry-fetch-average-rate: 500
#设置信任的client list
rate-limiter-privileged-clients: Collections.emptySet()
#在设置的时间范围类,期望与client续约的百分比。
renewal-percent-threshold: 0.85
#多长时间更新续约的阈值
renewal-threshold-update-interval-ms: 900000
#对于缓存的注册数据,多长时间过期
response-cache-auto-expiration-in-seconds: 180
#多长时间更新一次缓存中的服务注册数据
response-cache-update-interval-ms: 30000
#缓存增量数据的时间,以便在检索的时候不丢失信息
retention-time-in-m-s-in-delta-queue: 180000
#当时间戳不一致的时候,是否进行同步
sync-when-timestamp-differs: true
#是否采用只读缓存策略,只读策略对于缓存的数据不会过期。
use-read-only-response-cache: true
#============== server node 与 node 之间关联的配置 ==============#
#发送复制数据是否在request中,总是压缩
enable-replicated-request-compression: false
#指示群集节点之间的复制是否应批处理以提高网络效率。
batch-replication: false
#允许备份到备份池的最大复制事件数量。而这个备份池负责除状态更新的其他事件。可以根据内存大小,超时和复制流量,来设置此值得大小
max-elements-in-peer-replication-pool: 10000
#允许备份到状态备份池的最大复制事件数量
max-elements-in-status-replication-pool: 10000
#多个服务中心相互同步信息线程的最大空闲时间
max-idle-thread-age-in-minutes-for-peer-replication: 15
#状态同步线程的最大空闲时间
max-idle-thread-in-minutes-age-for-status-replication: 15
#服务注册中心各个instance相互复制数据的最大线程数量
max-threads-for-peer-replication: 20
#服务注册中心各个instance相互复制状态数据的最大线程数量
max-threads-for-status-replication: 1
#instance之间复制数据的通信时长
max-time-for-replication: 30000
#正常的对等服务instance最小数量。-1表示服务中心为单节点。
min-available-instances-for-peer-replication: -1
#instance之间相互复制开启的最小线程数量
min-threads-for-peer-replication: 5
#instance之间用于状态复制,开启的最小线程数量
min-threads-for-status-replication: 1
#instance之间复制数据时可以重试的次数
number-of-replication-retries: 5
#eureka节点间间隔多长时间更新一次数据。默认10分钟。
peer-eureka-nodes-update-interval-ms: 600000
#eureka服务状态的相互更新的时间间隔。
peer-eureka-status-refresh-time-interval-ms: 0
#eureka对等节点间连接超时时间
peer-node-connect-timeout-ms: 200
#eureka对等节点连接后的空闲时间
peer-node-connection-idle-timeout-seconds: 30
#节点间的读数据连接超时时间
peer-node-read-timeout-ms: 200
#eureka server 节点间连接的总共最大数量
peer-node-total-connections: 1000
#eureka server 节点间连接的单机最大数量
peer-node-total-connections-per-host: 10
#在服务节点启动时,eureka尝试获取注册信息的次数
registry-sync-retries: 0
#在服务节点启动时,eureka多次尝试获取注册信息的间隔时间
registry-sync-retry-wait-ms:
#当eureka server启动的时候,不能从对等节点获取instance注册信息的情况,应等待多长时间。
wait-time-in-ms-when-sync-empty: 02、Eureka Client 常用配置
eureka:
client:
#该客户端是否可用
enabled: true
#实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
register-with-eureka: false
#此客户端是否获取eureka服务器注册表上的注册信息,默认为true
fetch-registry: false
#是否过滤掉,非UP的实例。默认为true
filter-only-up-instances: true
#与Eureka注册服务中心的通信zone和url地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#client连接Eureka服务端后的空闲等待时间,默认为30 秒
eureka-connection-idle-timeout-seconds: 30
#client连接eureka服务端的连接超时时间,默认为5秒
eureka-server-connect-timeout-seconds: 5
#client对服务端的读超时时长
eureka-server-read-timeout-seconds: 8
#client连接all eureka服务端的总连接数,默认200
eureka-server-total-connections: 200
#client连接eureka服务端的单机连接数量,默认50
eureka-server-total-connections-per-host: 50
#执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10
cache-refresh-executor-exponential-back-off-bound: 10
#执行程序缓存刷新线程池的大小,默认为5
cache-refresh-executor-thread-pool-size: 2
#心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10
heartbeat-executor-exponential-back-off-bound: 10
#心跳执行程序线程池的大小,默认为5
heartbeat-executor-thread-pool-size: 5
# 询问Eureka服务url信息变化的频率(s),默认为300秒
eureka-service-url-poll-interval-seconds: 300
#最初复制实例信息到eureka服务器所需的时间(s),默认为40秒
initial-instance-info-replication-interval-seconds: 40
#间隔多长时间再次复制实例信息到eureka服务器,默认为30秒
instance-info-replication-interval-seconds: 30
#从eureka服务器注册表中获取注册信息的时间间隔(s),默认为30秒
registry-fetch-interval-seconds: 30
# 获取实例所在的地区。默认为us-east-1
region: us-east-1
#实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下
prefer-same-zone-eureka: true
# 获取实例所在的地区下可用性的区域列表,用逗号隔开。(AWS)
availability-zones:
china: defaultZone,defaultZone1,defaultZone2
#eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null
fetch-remote-regions-registry:
#服务器是否能够重定向客户端请求到备份服务器。 如果设置为false,服务器将直接处理请求,如果设置为true,它可能发送HTTP重定向到客户端。默认为false
allow-redirects: false
#客户端数据接收
client-data-accept:
#增量信息是否可以提供给客户端看,默认为false
disable-delta: false
#eureka服务器序列化/反序列化的信息中获取“_”符号的的替换字符串。默认为“__“
escape-char-replacement: __
#eureka服务器序列化/反序列化的信息中获取“$”符号的替换字符串。默认为“_-”
dollar-replacement: "_-"
#当服务端支持压缩的情况下,是否支持从服务端获取的信息进行压缩。默认为true
g-zip-content: true
#是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false
log-delta-diff: false
# 如果设置为true,客户端的状态更新将会点播更新到远程服务器上,默认为true
on-demand-update-status-change: true
#此客户端只对一个单一的VIP注册表的信息感兴趣。默认为null
registry-refresh-single-vip-address:
#client是否在初始化阶段强行注册到服务中心,默认为false
should-enforce-registration-at-init: false
#client在shutdown的时候是否显示的注销服务从服务中心,默认为true
should-unregister-on-shutdown: true3、Eureka Instance常用配置eureka:
instance:
#应用名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。
appname: unknown
#应用组名
appGroupName: null
#实例注册到Eureka上时,是否立刻开启通讯。有时候应用在准备好服务之前需要一些预处理。
instanceEnabledOnit: false
#非安全的端口
nonSecurePort: 80
#安全端口
securePort: 443
#是否开启非安全端口通讯
nonSecurePortEnabled: true
#是否开启安全端口通讯
securePortEnabled: false
#实例续约更新间隔时间
leaseRenewalIntervalInSeconds: 30
#实例超时/过期时间,表示最大leaseExpirationDurationInSeconds秒后没有续约,Server就认为他不可用了,随之就会将其剔除。
leaseExpirationDurationInSeconds: 90
#虚拟主机名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。
virtualHostName: unknown
#注册到eureka上的唯一实例ID,不能与相同appname的其他实例重复。
instanceId: ${spring.application.name}:${server.port}
#安全虚拟主机名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。
secureVirtualHostName: unknown
#实例元数据,可以供其他实例使用。比如spring-boot-admin在监控时,获取实例的上下文和端口。
metadataMap: new HashMap();
#实例部署的数据中心。如AWS、MyOwn。
dataCenterInfo: new MyDataCenterInfo(DataCenterInfo.Name.MyOwn);
#实例的IP地址
ipAddress: null
#实例状态页相对url
statusPageUrlPath: "/actuator/info"
#实例状态页绝对URL
statusPageUrl: null
#实例主页相对URL
homePageUrlPath: "/"
#实例主页绝对URL
homePageUrl: null
#实例健康检查相对URL
health-check-url-path: "/actuator/health"
#实例健康检查绝对URL
healthCheckUrl: null
#实例安全的健康检查绝对URL
secureHealthCheckUrl: null
#配置属性的命名空间(Spring Cloud中被忽略)
namespace: "eureka"
#主机名,不配置的时候将根据操作系统的主机名来获取
hostname: null
#是否优先使用IP地址作为主机名的标识
preferIpAddress: false
大数据需要拥抱云原生吗?云原生为什么这么火?(下)
四、云计算技术架构演进变革1、体系变革2、架构变革单体架构阶段集群架构阶段 分布式架构阶段分布式和集群的简单区别? 很多机器:都可以叫集群不同服务部署到不同服务器:才能称为分布式微服务架构阶段网格化架构阶段五、云上的挑战1、云上挑战云机器资源编排云存储方案云负载均衡方案云缓存方案云持久化云运维云监控云容器技术云DevOps云安全防护2、技术变革六、云原生的生态系统1、常见技术完整云原生平台基础研究量Docker、Docker Compose:容器化技术Kubernetes:大规模容器编排Helm:云原生应用商店Rancher:易用的容器管理平台KubeSphere:一站式容器云平台OpenTracing:云原生链路追踪标准Jaeger:云原生链路追踪实现产品Istio:ServiceMesh下的服务流量治理Jenkins、JenkinsX、Jenkins-BlueOcean:老牌的CI/CD平台Gitlab/hub-CICD:Gitlab/hub自带的CICDArgo:kubernetes声明式持续集成Nexus:Maven私库Habor:Docker私库Prometheus+Grafana:监控与可视化方案ElasticSearch+Fluentd+Kibana:日志与可视化方案Serverless:无服务器上云方案SpringCloud Kubernetes:微服务上云方案七、云原生的概念1、专业术语2、 云原生的定义云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基 础设施和声明式API这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。云原生计算基金会(CNCF)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。3、云原生学习路线图
《微服务零基础入门教程》一步一步,带你走进微服务的世界(下)
四、微服务案例搭建使用微服务架构的分布式系统,微服务之间通过网络通信,我们通过服务提供者与服务消费者来描述微服务间的调用关系。服务提供者:服务的被调用方,提供调用接口的一方服务消费者:服务的调用方,依赖于其他服务的一方我们以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。在这种场景下,订单微服务就是一个服务消费者,商品微服务就是一个服务提供者。1、搭建环境创建父工程在IDEA中创建父工程并引入坐标<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>service-product</module>
<module>service-order</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.uncle</groupId>
<artifactId>micro-service-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>micro-service-demo</name>
<description>micro-service-demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建微服务工程模块2、搭建商品微服务编写实体类@Data
@Entity
@Table(name="tb_product")
public class Product {
@Id
private Long id;
private String productName;
private Integer status;
private BigDecimal price;
private String productDesc;
private String caption; }这里使用了lombok简化实体类的开发Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率编写dao接口public interface ProductDao extends JpaRepository<Product,Long> ,
JpaSpecificationExecutor<Product> {}编写service层public interface ProductService {
//根据id查询
Product findById(Long id);
//查询全部
List findAll();
//保存
void save(Product product);
//更新
void update(Product product);
//删除
void delete(Long id);
}@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findById(Long id) {
return productDao.findById(id).get();
}
@Override
public List findAll() {
return productDao.findAll();
}
@Override
public void save(Product product) {
productDao.save(product);
}
@Override
public void update(Product product) {
productDao.save(product);
}
@Override
public void delete(Long id) {
productDao.deleteById(id);
}
}编写web层@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public List findAll() {
return productService.findAll();
}
@GetMapping("/{id}")
public Product findById(@PathVariable Long id) {
return productService.findById(id);
}
@PostMapping
public String save(@RequestBody Product product) {
productService.save(product);
return "保存成功";
}
@PutMapping("/{id}")
public String update(@RequestBody Product product) {
productService.update(product);
return "修改成功";
}
@DeleteMapping("/{id}")
public String delete(Long id) {
productService.delete(id);
return "删除成功";
}
}controller中使用的@GetMapping是一个组合注解,相当与@RequestMapping(method=“get”)。类似的注解还有@PostMapping,@PutMapping,@DeleteMapping配置启动类package com.uncle.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author sjx
*/
@SpringBootApplication
@EntityScan("")
public class ProductApplication {
/**
* main 方法
* @param args:参数列表
*/
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
配置yml文件server:
port: 9002
spring:
application:
name: shop-service-product
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
username: root
password: 111111
jpa:
database: MySQL
show-sql: true
open-in-view: true3、搭建订单微服务编写实体类@Data
@Entity
@Table(name="tb_order")
public class Order {
@Id
private Long id;
private String productName;
private Integer status;
private BigDecimal price;
private String orderDesc;
private String caption; }这里使用了lombok简化实体类的开发Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率编写dao接口public interface OrdertDao extends JpaRepository<Order,Long> ,
JpaSpecificationExecutor<Order> {}编写service层public interface OrderService {
//根据id查询
Order findById(Long id);
//查询全部
List findAll();
//保存
void save(Order order);
//更新
void update(Order order);
//删除
void delete(Long id);
}@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao productDao;
@Override
public Order findById(Long id) {
return productDao.findById(id).get();
}
@Override
public List findAll() {
return productDao.findAll();
}
@Override
public void save(Product product) {
productDao.save(product);
}
@Override
public void update(Product product) {
productDao.save(product);
}
@Override
public void delete(Long id) {
productDao.deleteById(id);
}
}编写web层@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public List findAll() {
return productService.findAll();
}
@GetMapping("/{id}")
public Product findById(@PathVariable Long id) {
return productService.findById(id);
}
@PostMapping
public String save(@RequestBody Product product) {
productService.save(product);
return "保存成功";
}
@PutMapping("/{id}")
public String update(@RequestBody Product product) {
productService.update(product);
return "修改成功";
}
@DeleteMapping("/{id}")
public String delete(Long id) {
productService.delete(id);
return "删除成功";
}
}controller中使用的@GetMapping是一个组合注解,相当与@RequestMapping(method=“get”)。类似的注解还有@PostMapping,@PutMapping,@DeleteMapping配置启动类package com.uncle.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author sjx
*/
@SpringBootApplication
@EntityScan("")
public class OrderApplication {
/**
* main 方法
* @param args:参数列表
*/
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
配置yml文件server:
port: 9001
spring:
application:
name: shop-service-order
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
username: root
password: 111111
jpa:
database: MySQL
show-sql: true
open-in-view: true4、服务调用RestTemplate介绍Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。RestTemplate方法介绍通过RestTemplate调用微服务配置RestTemplate//配置RestTemplate交给spring管理
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}编写下订单方法@PostMapping("/{id}")
public String order(Integer num) {
//通过restTemplate调用商品微服务
Product object =
restTemplate.getForObject("http://127.0.0.1:9002/product/1", Product.class);
System.out.println(object);
return "操作成功";
}硬编码存在的问题至此已经可以通过RestTemplate调用商品微服务的RESTFul API接口。但是我们把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:应用场景有局限无法动态调整那么应该怎么解决呢,就需要通过注册中心动态的对服务注册和服务发现
《微服务零基础入门教程》一步一步,带你走进微服务的世界(上)
版本选择版本选择,本人:使用如下最新版本一定要注意版本适配一、系统架构的演变随着互联网的发展,网站应用的规模不断扩大,常规的应用架构已无法应对,分布式服务架构以及微服务架构势在必行,极需一个治理系统确保架构有条不紊的演进。1、单体应用架构Web应用程序发展的早期,大部分web工程(包含前端页面,web层代码,service层代码,dao层代码)是将所有的功能模块,打包到一起并放在一个web容器中运行。所有功能都部署在一个web容器中运行的系统就叫做单体架构。优点:所有的功能集成在一个项目工程中项目架构简单,前期开发成本低,周期短,小型项目的首选。缺点:全部功能集成在一个工程中,对于大型项目不易开发、扩展及维护。系统性能扩展只能通过扩展集群结点,成本高、有瓶颈。技术栈受限。2、垂直应用架构当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。优点:项目架构简单,前期开发成本低,周期短,小型项目的首选。通过垂直拆分,原来的单体项目不至于无限扩大不同的项目可采用不同的技术。缺点:全部功能集成在一个工程中,对于大型项目不易开发、扩展及维护。系统性能扩展只能通过扩展集群结点,成本高、有瓶颈。3、分布式SOA架构什么是SOASOA 全称为 Service-Oriented Architecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。站在功能的角度,把业务逻辑抽象成可复用、可组装的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。通过上面的描述可以发现 SOA 有如下几个特点:分布式、可重用、扩展灵活、松耦合SOA架构当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。优点:抽取公共的功能为服务,提高开发效率对不同的服务进行集群化部署解决系统压力基于ESB/DUBBO减少系统耦合缺点:抽取服务的粒度较大服务提供方与调用方接口耦合度较高4、微服务架构优点:通过服务的原子化拆分,以及微服务的独立打包、部署和升级,小团队的交付周期将缩短,运维成本也将大幅度下降微服务遵循单一原则。微服务之间采用Restful等轻量协议传输。缺点:微服务过多,服务治理成本高,不利于系统维护。分布式系统开发的技术成本高(容错、分布式事务等)。5、SOA与微服务的关系SOA( Service Oriented Architecture )“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。二、分布式核心知识1、分布式中的远程调用在微服务架构中,通常存在多个服务之间的远程调用的需求。远程调用通常包含两个部分:序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等,目前主流的远程调用技术有基于HTTP的RESTful接口以及基于TCP的RPC协议。RESTful接口REST,即Representational State Transfer的缩写,如果一个架构符合REST原则,就称它为RESTful架构。资源(Resources)??所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。REST的名称"表现层状态转化"中,省略了主语。“表现层"其实指的是"资 源”(Resources)的"表现层"。表现层(Representation)?“资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层”(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。状态转化(State Transfer)?访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。综合上面的解释,我们总结一下什么是RESTful架构:?每一个URI代表一种资源;客户端和服务器之间,传递这种资源的某种表现层;客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。RPC协议RPC(Remote Procedure Call ) 一种进程间通信方式。允许像调用本地服务一样调用远程服务。RPC框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。RESTful和RPC之间的区别与联系1、HTTP相对更规范,更标准,更通用,无论哪种语言都支持http协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,现在开源中间件,基本最先支持的几个协议都包含RESTful。2、 RPC 框架作为架构微服务化的基础组件,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节。让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务。2、分布式中的CAP原理现如今,对于多数大型互联网应用,分布式系统(distributed system)正变得越来越重要。分布式系统的最大难点,就是各个节点的状态如何同步。CAP 定理是这方面的基本定理,也是理解分布式系统的起点。CAP理论由 Eric Brewer 在ACM研讨会上提出,而后CAP被奉为分布式领域的重要理论。分布式系统的CAP理论,首先把分布式系统中的三个特性进行了如下归纳:Consistency(一致性):数据一致更新,所有数据的变化都是同步的Availability(可用性):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求 Partitiontolerance(分区容忍性):某个节点的故障,并不影响整个系统的运行通过学习CAP理论,我们得知任何分布式系统只可同时满足二点,没法三者兼顾,既然一个分布式系统无法同时满足一致性、可用性、分区容错性三个特点,所以我们就需要抛弃一样:需要明确一点的是,在一个分布式系统当中,分区容忍性和可用性是最基本的需求,所以在分布是系统中,我们的系统最当关注的就是A(可用性)P(容忍性),通过补偿的机制寻求数据的一致性。?三、常见微服务框架1、SpringCloudSpring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。2、SpringCloud AlibabaSpring Cloud alibaba为分布式应用开发提供一站式解决方案。它包含开发分布式应用程序所需的所有组件,使您可以轻松地使用Spring Cloud开发应用程序。有了Spring Cloud Alibaba,您只需要添加一些注释和少量配置,就可以将Spring Cloud的应用程序连接到阿里巴巴的分布式解决方案上,并利用阿里巴巴的中间件构建分布式应用系统。主要功能服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring CloudGateway, Zuul, Dubbo 和 RocketMQ限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 服务注册与发现:适配Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。 消息驱动能力:基于 Spring Cloud Stream为微服务应用构建消息驱动能力。 分布式事务:使用 @GlobalTransactional 注解,高效并且对业务零侵入地解决分布式事务问题。阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker(schedulerx-client)上执行。阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。3、ServiceCombApache ServiceComb 是业界第一个Apache微服务顶级项目, 是一个开源微服务解决方案,致力于帮助企业、用户和开发者将企业应用轻松微服务化上云,并实现对微服务应用的高效运维管理。其提供一站式开源微服务解决方案,融合SDK框架级、0侵入ServiceMesh场景并支持多语言。【感兴趣的同学可以进官网学习一下】4、ZeroC ICEZeroC IceGrid 是ZeroC公司的杰作,继承了CORBA的血统,是新一代的面向对象的分布式系统中间件。作为一种微服务架构,它基于RPC框架发展而来,具有良好的性能与分布式能力。5、拓展框架Java语言相关微服务框架DubboDubbo是由阿里巴巴开源的分布式服务化治理框架,通过RPC请求方式访问。Dubbo是在阿里巴巴的电商平台中逐渐探索演进所形成的,经历过复杂业务的高并发挑战,比Spring Cloud的开源时间还要早。目前阿里、京东、当当、携程、去哪等一些企业都在使用Dubbo。DropwizardDropwizard将Java生态系统中各个问题域里最好的组建集成于一身,能够快速打造一个Rest风格的后台,还可以整合Dropwizard核心以外的项目。国内现在使用Dropwizard还很少,资源也不多,但是与Spring Boot相比,Dropwizard在轻量化上更有优势,同时如果用过Spring,那么基本也会使用Spring Boot。AkkaAkka是一个用Scala编写的库,可以用在有简化编写容错、高可伸缩性的Java和Scala的Actor模型,使用Akka能够实现微服务集群。Vert.x/Lagom/ReactiveX/Spring 5这四种框架主要用于响应式微服务开发,响应式本身和微服务没有关系,更多用于提升性能上,但是可以和微服务相结合,也可以提升性能。.Net相关微服务框架.NET Core.NET Core是专门针对模块化微服务架构设计的,是跨平台应用程序开发框架,是微软开发的第一个官方版本。Service FabricService Fabric是微软开发的一个微服务框架,基于Service Fabric构建的很多云服务被用在了Azure上。SurgingSurging是基于RPC协议的分布式微服务技术框架,基于.NET Core而来。Microdot FrameworkMicrodot Framework用于编写定义服务逻辑代码,不需要解决开发分布式系统的挑战,能够很方便的进行MicrosoftOrleans集成。Node.js相关微服务框架SenecaSeneca是Node.js的微服务框架开发工具,可以用于编写可用于产品环境的代码。Hapi/Restify/LoopBack这三种框架的分工不同,前两种更适合开发简单的微服务后端系统,第三种更适合用在大型复杂应用开发,还可以用在现有微服务上的构建。Go相关微服务框架Go-Kit/Goa/DubbogoGo-Kit是分布式开发的工具合集,适合用于大型业务场景下构建微服务;Goa是用Go语言构建的微服务框架;Dubbogo是和阿里巴巴开源的Dubbo能够兼容的Golang微服务框架。Python相关微服务框架Python相关的微服务框架非常少,用的比较多的是Nameko。Nameko让实现微服务变得更简单,同时也提供了很丰富的功能,比如支持负载均衡、服务发现还支持依赖自动注入等,使用起来很方便,但是有限速、超时和权限机制不完善等缺点。
Node.js专题讲解 第2期 package.json
一、前言随着前端工程化、自动化的发展,模块化开发已经成为当下前端的一种潮流,而在一个完整的Node.js项目中,package.json 文件无处不在。首先,在项目根目录会有,其次在 node_modules 中也频现。那么这个文件到底是干嘛的,又有什么作用,今天给大家揭晓。为了回馈粉丝的支持,前端微服务特开启Node.js专题,系统学习Node.js知识,为还在迷茫路上的你,提供系统化的学习。二、学到知识点了解 package.json的作用了解 创建package.json的方法了解package.json与项目之间的关系确定重要字段和元数据了解如何管理 package.json了解package.json和package-lock.json的区别三、package.json 文件作用我们在创建 Node.js项目的时候,会遇到 package.json 文件。它是一个 JSON 文件,位于项目的根目录下。package.json 包含关于项目的重要信息。它包含关于项目的使人类可读元数据(如项目名称和说明)以及功能元数据(如程序包版本号和程序所需的依赖项列表),要想彻底搞懂package.json,我们还得从Node.js的模块(Module)讲起,在Node.js中,模块可以是一个库或者一个框架,也可以是一个Node.js项目,Node.js项目遵循模块化架构,当我们创建一个Node.js项目的时,这些模块的描述文件被称之为package.json文件,package.json是Node.js项目中非常重要的配置文件,是一个项目的核心,该文件中跟踪依赖关系和元数据,定义了当前项目所需要的各种模块,以及项目的配置信息,是对项目或者模块包的描述,里面包含许多元信息。其定义了运行项目所需要的各种依赖和项目的配置信息(比如项目名称、项目版本、项目执行入口文件、执行的脚本、依赖的插件、项目贡献者、许可证以及运行、开发以及有选择地将项目发布到 npm 所需的信息。)。它是配置和描述如何与程序交互和运行的中心,npm和yarn用它来识别你的项目并了解如何处理项目的依赖关系。 npm cli 也是管理 package.json 的最佳方法,因为它有助于在项目的整个生命周期内生成和更新 package.json 文件。package.json文件就是一个JSON对象,该对象的每一个成员就是当前项目的一项设置。package.json 会在项目的生命周期中扮演多个角色,其中某些角色仅适用于发布到 npm的软件包,可以启动你的项目、运行脚本、安装依赖项、发布到npm注册表以及许多其他有用的任务。。即使你没有把项目发布到 npm注册表中,或者没有将其公开发布给其他人,那么 package.json 对于开发流程仍然至关重要。你的项目还必须包含 package.json,然后才能从 npm安装软件包。这也是项目中需要它的主要原因之一。npm install 命令会根据这个文件下载所有依赖模块。package.json一般都在项目的根目录下。四、package.json 文件创建package.json 文件创建有两种方式,手动创建和自动创建,一般我们使用自动创建较多。4.1手动创建创建项目目录$mkdir node-demo>cd node-demo>touch package.json直接在项目根目录下新建一个 package.json 文件,然后输入相关的内容。{
"name": "express-admin",
"version": "1.0.0",
"description": "Express Admin",
"keywords": [
"server",
"express",
"compression"
],
"homepage":"https://gitee.com/xunzhaotech/express-admin.git",
"bugs":"https://github.com/owner/project/issues",
"license": "MIT",
"author": {
"name": "Amber luyb",
"email": "luyb@xunzhaotech.com",
"url": "https://www.xunzhaotech.com"
},
"contributors": [
{
"name": "Amber luyb",
"email": "luyb@xunzhaotech.com",
"url": "https://gitee.com/xunzhaotech/express-admin.git"
}
],
"funding": [
{
"type" : "individual",
"url" : "http://example.com/donate"
},
"http://example.com/donateAlso",
{
"type" : "patreon",
"url" : "https://www.patreon.com/my-account"
}
],
"files":[".git",
"CVS",
".svn",
".hg",
".lock-wscript",
".wafpickle-N",
".*.swp",
".DS_Store",
"._*",
"npm-debug.log",
".npmrc",
"node_modules",
"config.gypi",
"*.orig,"],
"main": "index.js",
"browser":"",
"bin": {
"myapp": "./cli.js"
},
"man": [
"./man/foo.1",
"./man/bar.1"
],
"repository": {
"type": "git",
"url": "https://gitee.com/xunzhaotech/express-admin.git"
},
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs",
"server": "node server.js",
"start": "node index.js",
"dev": "nodemon",
"lint": "eslint **/*.js"
},
"config": {
"port": "8080"
},
"devDependencies": {
"eslint": "^7.22.0",
"mockjs": "^1.1.0",
"nodemon": "^2.0.7",
"vuepress": "^1.8.2"
},
"dependencies": {
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"debug": "^4.3.1",
"express": "^4.17.1",
"express-session": "^1.17.1"
},
"peerDependencies": {
"tea": "2.x"
},
"peerDependenciesMeta": {
"soy-milk": {
"optional": true
}
},
"bundledDependencies": [
"renderized",
"super-streams"
],
"optionalDependencies":{},
"engines": {
"npm": "~1.0.20"
},
"os": [
"darwin",
"linux"
],
"cpu": [
"!arm",
"!mips"
],
"private": false,
"publishConfig":{},
"workspaces": [
"./packages/*"
]
}4.2自动创建当我们新建一个名称为 xz-nuxt-admin, 在项目根目录下执行 yarn init -y 或 npm init -y 命令后,也可以输入 npm init或yarn init -y这个命令采用互动方式,要求用户回答一些问题,然后根据提示一步步输入相应的内容完成后即可在项目目录下会新增一个基本的 package.json文件。所有问题之中,只有项目名称(name)和项目版本(version)是必填的,其他都是选填的。内容如下:{
"name": "my-test", # 项目名称
"version": "1.0.0", # 项目版本(格式:大版本.次要版本.小版本)
"author": "", # 作者
"description": "", # 项目描述
"main": "index.js", # 入口文件
"scripts": { # 指定运行脚本命令的 npm 命令行缩写
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], # 关键词
"license": "ISC" # 许可证
}五、package.json 文件配置说明我们知道package.json 中包含的一些最常见和重要的字段,用来管理项目的配置信息或发布到 npm,而其他一些则可以帮助 npm CLI 运行应用程序或安装依赖项。实际的字段比我们所介绍的要多,你可以在它的文档中了解其余字段,但以下的是必须要了解的 package.json 属性。5.1 必须字段package.json 中有非常多的配置项,其中必须填写的两个字段分别是 name 字段和 version 字段,它们是组成一个 npm 模块的唯一标识。namename 字段定义了模块的名称,其命名时需要遵循官方的一些规范和建议:项目/模块名称,长度必须小于等于214个字符,不能以"."(点)或者"_"(下划线)开头,不能包含大写字母。模块名会成为模块 url、命令行中的一个参数或者一个文件夹名称,任何非 url 安全的字符在模块名中都不能使用(我们可以使用 validate-npm-package-name 包来检测模块名是否合法)。语义化模块名,可以帮助开发者更快的找到需要的模块,并且避免意外获取错误的模块;若模块名称中存在一些符号,将符号去除后不得与现有的模块名重复。name 字段不能与其他模块名重复,我们可以执行以下命令查看模块名是否已经被使用:npm view 或者,我们也可以去 npm 上输入模块名,如果搜不到,则可以使用该模块名。version在package.json文件中,版本是非常重要的一个概念,npm 包中的模块版本都需要遵循 SemVer 规范,该规范的标准版本号采用 X.Y.Z 的格式,其中 X、Y 和 Z 均为非负的整数,且禁止在数字前方补零:X 是主版本号(major):第一位就是主要版本号。一般来说,重大功能的更新,或功能的添加又不能向后兼容的情况会提升该版本号。Y 是次版本号(minor):中间那一位是指次要版本号。一般来说,一些功能的添加,但又能向后兼容,这种更新会提升该版本号。Z 是修订号(patch):最后一位就是补丁版本号。一般来说,如果是项目的一些BUG修复,会将该版本号提升。当某个版本改动比较大、并非稳定而且可能无法满足预期的兼容性需求时,我们可能要先发布一个先行版本。先行版本号可以加到主版本号.次版本号.修订号的后面,通过 - 号连接一连串以句点分隔的标识符和版本编译信息:内部版本(alpha)公测版本(beta)正式版本的候选版本rc(即 Release candiate)我们可以执行以下命令查看模块的版本:$npm view <packageName> version # 查看某个模块的最新版本
$npm view <packageName> versions # 查看某个模块的所有历史版本5.2 可选字段可选字段是除去必填字段需要补充的项目信息描述信息(description & keywords)description:字段用于添加模块的描述信息,便于用户了解该模块,是一个字符串。它可以帮助人们在使用npm search时找到这个包。keywords 项目关键字,是一个字符串数组。用于给模块添加关键字。它可以帮助人们在使用npm search时找到这个包。当我们使用 npm 检索模块时,会对模块中的 description 字段和 keywords 字段进行匹配,写好 package.json中的 description 和 keywords 将有利于增加我们模块的曝光率。安装项目依赖(dependencies & devDependencies)dependencies:字段指定了项目运行所依赖的模块(生产环境下,项目运行所需依赖)。devDependencies:指定项目开发所需要的模块(开发环境下,项目所需依赖)。说明:我们在使用vue框架开发一个程序,开发阶段需要用到webpack来构建你的开发和本地运行环境。所以vue一定要放到dependencies里,因为以后程序到生产环境也要用。webpack则是你用来压缩代码,打包等需要的工具,程序实际运行的时候并不需要,所以放到devDependencies里就可以了。或者我们在写程序要用ES6标准,浏览器并不完全支持,所以你要用到babel来转换代码,babel放devDependencies。程序里有用到开源组件,比如你想用antd,antd要放dependencies。简化终端命令(scripts)scripts 字段是 package.json 中的一种元数据功能,它接受一个对象,对象的属性为可以通过 npm run 运行的脚本,值为实际运行的命令(通常是终端命令),如:"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
},将终端命令放入 scripts 字段,既可以记录它们又可以实现轻松重用。定义项目入口(main)main 字段是 package.json 中的另一种元数据功能,它可以用来指定加载的入口文件。假如你的项目是一个 npm 包,当用户安装你的包后,const ModuleName = require('module-name') 返回的是 main 字段中所列出文件的 module.exports 属性。当不指定main 字段时,默认值是模块根目录下面的index.js 文件。发布文件配置(files)files 字段用于描述我们使用 npm publish 命令后推送到 npm 服务器的文件列表,如果指定文件夹,则文件夹内的所有内容都会包含进来。我们可以查看下载的 antd 的 package.json 的files 字段,内容如下:"files": [
"dist",
"lib",
"es"
……
],可以看到下载后的 antd 包是下面的目录结构中包含了 /dist/"lib/es文件,另外,我们还可以通过配置一个 .npmignore 文件来排除一些文件, 防止大量的垃圾文件推送到 npm 上。定义私有模块(private)一般公司的非开源项目,都会设置 private 属性的值为 true,这是因为 npm 拒绝发布私有模块,通过设置该字段可以防止私有模块被无意间发布出去。指定模块适用系统(os)使用 os 属性可以指定模块适用系统的系统,或者指定不能安装的系统黑名单(当在系统黑名单中的系统中安装模块则会报错)"os" : [ "darwin", "linux" ] # 适用系统
"os" : [ "!win32" ] # 黑名单在 node 环境下可以使用 process.platform 来判断操作系统指定模块适用 cpu 架构(cpu)我们可以用 cpu 字段更精准的限制用户安装环境"cpu" : [ "x64", "ia32" ] # 适用 cpu
"cpu" : [ "!arm", "!mips" ] # 黑名单在 node 环境下可以使用 process.arch 来判断 cpu 架构指定项目 node 版本(engines)防止因node 版本不同,导致会出现很多奇奇怪怪的问题(如某些依赖安装报错、依赖安装完项目跑不起来等)。"engines": {
"node": ">= 8.16.0",
"npm": ">= 6.9.0"
},需要注意的是,engines属性仅起到一个说明的作用,当用户版本不符合指定值时也不影响依赖的安装自定义命令(bin)bin 字段用来指定各个内部命令对应的可执行文件的位置。主要应用在脚手架搭建中,当package.json 提供了 bin 字段后,即相当于做了一个命令名和本地文件名的映射。 当用户安装带有 bin 字段的包时,如果是全局安装,npm 将会使用符号链接把这些文件链接到/usr/local/node_modules/.bin/;如果是本地安装,会链接到./node_modules/.bin/。如果要使用 mfd-cli 作为命令时,可以配置以下 bin 字段:"bin": {
"mfd-cli": "./bin/cli.js"
}上面代码指定,mfd-cli 命令对应的可执行文件为 bin 子目录下的 cli.js,因此在安装了 mfd-cli 包的项目中,就可以很方便地利用 npm执行脚本:"scripts": {
start: 'node node_modules/.bin/mfd-cli'
}这里看起来和 vue create/create-react-app之类的命令不太一样?是因为:当需要 node 环境时就需要加上 node 前缀如果加上 node 前缀,就需要指定mfd-cli 的路径 -> node_modules/.bin,否则 node mfd-cli会去查找当前路径下的 mfd-cli.js,这样肯定是不对。若要实现像 vue create/create-react-app之类的命令一样简便的方式,则可以在上文提到的 bin 子目录下可执行文件cli.js 中的第一行写入以下命令:#!/usr/bin/env node这行命令的作用是告诉系统用 node 解析,这样命令就可以简写成 mfd-cli 了。设置应用根路径(homepage)当我们使用 create-react-app 脚手架搭建的 React 项目,默认是使用内置的 webpack 配置,当package.json 中不配置 homepage 属性时,build 打包之后的文件资源应用路径默认是/。一般来说,我们打包的静态资源会部署在 CDN 上,为了让我们的应用知道去哪里加载资源,则需要我们设置一个根路径,这时可以通过 package.json 中的 homepage 字段设置应用的根路径。当我们设置了 homepage 属性后:{
"homepage": "https://xxxx.cdn/project-name",
}打包后的资源路径就会加上 homepage 的地址:/project-name/bundle.js其它配置author:项目开发者,它的值是你在npmjs.org网站的有效账户名,遵循“账户名<邮件>”的规则,例如:xunzhaotech@aliyun.com。private:是否私有,设置为 true 时,npm 拒绝发布。license:软件授权条款,让用户知道他们的使用权利和限制。bugs:bug 提交地址。contributors:项目贡献者 。repository:项目仓库地址。module:是以 ES Module(也就是 ES6)模块化方式进行加载,因为早期没有 ES6 模块化方案时,都是遵循 CommonJS 规范,而 CommonJS 规范的包是以 main 的方式表示入口文件的,为了区分就新增了 module 方式,但是 ES6 模块化方案效率更高,所以会优先查看是否有 module 字段,没有才使用 main 字段。eslintConfig:EsLint 检查文件配置,自动读取验证。browserslist:供浏览器使用的版本列表。style:供浏览器使用时,样式文件所在的位置;样式文件打包工具parcelify,通过它知道样式文件的打包位置。六、package.json和package-lock.json的区别为什么有了package.json,还需要package-lock.json文件呢,当node_modules文件夹并不存在或被删除时,需要用到npm install重新装载全部依赖时,通过package-lock.json可以直接表明下载地址和相关依赖,相对下载速度也更快,也不容易报错。
Node.js专题讲解 第1期 概念篇
一、前言经过这几年的发展,前端普遍进入了技术深水区,只会Web页面开发已经难以满足企业的需求,Node逐渐成为了刚性技能。为了回馈粉丝的支持,前端微服务特开启Node.js专题,系统学习Node.js知识,为还在迷茫路上的你,提供系统化的学习。二、Node.js发展史Ryan Dahl,一名资深的C/C++程序员,他的主要工作是用C/C++写高性能Web服务。在经历过一些尝试?和失败??之后,他找到了设计高性能,Web服务器的几个要点:事件驱动、非阻塞I/O。Ryan Dahl最初的目标是写一个基于事件驱动、非阻塞I/O的Web服务器,以达到更高的性能,提供Apache等服务器之外的选择。但是用C/C++写就太痛苦了。于是这位兄弟开始设想用高级语言开发Web服务。他评估了很多种高级语言,发现JavaScript比C的开发门槛要低,比Lua的历史包袱要少。尽管服务器端JavaScript存在已经很多年了,但是后端部分一直没有市场,并且空白,可以说历史包袱为零,JavaScript是单线程执行,根本不能进行同步IO操作,为其导入非阻塞I/O库没有额外阻力。所以选定JavaScript成为了Node.js的实现语言。当时,第二次浏览器大战也渐渐分出高下,Chrome浏览器的JavaScript引擎V8摘得性能第一的宝座。又因为V8就是开源的JavaScript引擎,有着Google这一个大团队投资和持续支持,于是选择V8作为运行时引擎为不二之选。于是在2009年,Ryan正式推出了基于JavaScript语言和V8引擎的开源Web服务器项目,命名为Node.js。虽然名字很土,但是,Node第一次把JavaScript带入到后端服务器开发,加上世界上已经有无数的JavaScript开发人员,所以Node.js一下子就火了起来。起初,Ryan Dahl称他的项目为web.js,就是一个Web服务器,但是项目的发展超过了他最初单纯开发一个Web服务器的想法,变成了构建网络应用的一个基础框架,这样可以在它的基础上构建更多的东西,诸如服务器、客户端、命令行工具等。三、Node.js是什么读完Node.js,读完了它的发展由来。Node.js 不是一门新的语言,也不是一个JavaScript的框架,它是一个JavaScript运行在浏览器之外的平台,是运行在服务端的 JavaScript或者Node.js 是一个服务器端 JavaScript 解释器,是一个Javascript运行环境(runtime)。它采用事件驱动、异步编程、为网络服务而设计,实现了诸如文件系统、模块、包、操作系统 API、网络通信等 Core JavaScript 没有或者不完善的功能。实际上它是对Google V8引擎进行了封装。V8引擎执行Javascript的速度非常快,性能非常好。Node.js对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。说白了,就是一个能编译你写的js代码并让计算机识别并执行的编译环境,一般我们会用的到浏览器的内核来编译解析我们的JS代码,而Node.js是另一种运行环境(所以Node.js是一个平台,不是一种语言,更不是什么后端语言,也不是什么前端框架,它是由C++编写的JavaScript的运行环境—— 虚拟机)。四、Node.js应用场景Node.js可以是前端全栈的基石,可以与前端衔接,用来做后端应用。它的应用场景已经从前端工具的脚手架、辅助工具类,发展到api中间层、代理层,以及专业的后端开发。这其中最有价值的就是api中间层。前后端协作的时候,因为进度不同有时前端需要等后端的接口。为了保证进度,前端可以用node做api中间层,通过nodeJs可以自己约定数据的格式,根据前端的需求定制自己的接口,自己封装对数据库的增删改查操作,创建自己的代理和网关服务器等。这些事情在以往只能是等着后端做,但现在通过nodeJs,前端人员都可以自己搞定后端服务这个事情,这就使得工作的过程更加可控。有时候,仅仅使用Node.js可以作为一个中间层来来分发调用数据接口,比如有一个网站数据是有java提供的,我们可以让Node.js作为一个中间层,来接受用户的请求,然后通过Node.js来调用java数据接口,获取到数据后直接在Node.js层面做html组合,然后将渲染好的页面直接给用户。为什么要这样做,直接请求java接口不行吗,这是因为Node.js被称之为高性能的web服务器,在并发和抗压方面都比传统的平台要好很多,因此这样一包装可以极大的减轻服务器的开发。五、Node.js的优点Node.js最大的特点是采用异步式I\O与事件驱动单线程异步的非阻塞I/O事件驱动解析JS代码没有浏览器安全级的限制;提供系统级别的API;a. 文件的读写b. 进程的管理c. 网络通信Node.js库异步和事件驱动Node.js 使用的是单线程模型,对于所有I/O都采用异步式的请求方式,避免了频繁的上下文切换。Node.js 在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式 I/O 请求完成后会被推送到事件队列,等待程序进程进行处理。所有API异步是非阻塞。 这意味着一个基于Node.js的服务器不会等待API返回数据。 服务器移动到下一个API后调用它,Node.js事件的一个通知机制有助于服务器,以获得从以API调用的响应。Node.js 进程在同一时刻只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件。这样做的好处是,CPU和内存在同一时间集中处理一件事,同时尽可能让耗时的I/O操作并行执行。对于低速连接攻击,Node.js只是在事件队列中增加请求,等待操作系统的回应,因而不会有任何多线程开销,很大程度上可以提高 Web 应用的健壮性,防止恶意攻击。非常快正在构建在谷歌Chrome的V8 JavaScript引擎,Node.js库代码执行是非常快的。单线程但高度可扩展Node.js使用事件循环单线程模型。事件机制有助于服务器在非阻塞的方式作出反应,并使得服务器的高可扩展性,而不是它创建线程限制来处理请求的传统服务器。 Node.js使用单线程的程序和同样的程序处理比传统的服务器要大的多,比如:比Apache HTTP服务器请求服务的数量大得多。无缓冲Node.js的应用从来没有缓冲任何数据。这些应用程序只需输出块中的数据。(即流应用)许可证Node.js是在MIT许可下发布的。六、Node能做什么具有复杂逻辑的网站基于社交网络的大规模web应用web Socket网站TCP\UDP套接字应用程序命令行工具交互式终端程序带有图形用户界面的本地应用程序单元测试工具客户端Javascript编译器……七、相关概念npmpackage.jsonexpresskoaegg
2020你应该知道的TypeScript学习路线【函数类型】
一、概述函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。它在编程范式中为我们控制行为,抽象某些实现有着较大的帮助,因此 TypeScript 在 JavaScript 的基础之上添加了一些额外的功能,辅助我们更好的使用函数。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。今天小编就带大家一起来学习下TypeScript的函数。如果大家觉得有帮助,记得给小编点个赞,如果想获取更多干货请关注前端微服务公众号,不定期为大家带来干货。二、与JavaScript区别和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式,不论是定义一系列API函数还是只使用一次的函数。三、函数定义函数就是包裹在花括号中的代码块,前面使用了关键词 function,语法格式如下所示:function function_name()
{
// 执行代码
}四、调用函数函数只有通过调用才可以执行函数内的代码。语法格式如下所示:function_name()五、使用案例5.1 基础声明// javascript
function sum(x,y) {
return x + y;
}// typescript
function sum(x: number, y: number): number {
return x + y;
}// 表达式
const sum = function (x: number, y: number): number {
return x + y;
}不过由于 TypeScript 可以根据 return 来推断返回的类型,因此有时候返回类型 number 并不是一定要书写的,不过为了给人阅读时不增加不必要的开支,写上明确的返回类型,是一种非常好的习惯。还有一点的是,虽然上述的代码我们已经为其添加了类型,不过这还不是一个很完整的类型书写,但往往我们可以忽略它,如:type Sum = (x: number, y: number) => number;
const sum: Sum = function(x: number, y: number): number {
return x + y;
}
复制代码以往我们写 JavaScript 时调用 sum 函数,也许我们什么都不传,或者只传一个,但在 TypeScript 的世界中参数是必须真实存在的,我们调用时必须传递两个参数,因此我们可以使用 可选参数 和 默认参数 的方式来处理这个问题,如:function sum(x: number, y?: number): number {
}
function sum(x: number, y = 0): number {
}5.2 TypeScript中arguments 处理function sum(...num: number[]): number {
return num[0] + num[1];
}5.3 TypeScript中this的使用let obj = {
a: 1,
b: function (){
return () => {
return this.a;
}
}
}如果你没有使用箭头函数,那么这里的 this.a 会得到一个错误,因为此时的 this 会被设置为一个全局的对象,浏览器中是 window ,Node.js里是 global。5.4 TypeScript重载功能function sync(x: number): number;
function sync(x: { code: number, sig: string}): string;
function sync(x: any): any {
if (typeof x === "number") {
return 0;
}
if (typeof x === "string") {
return "0";
}
}
sync(0);5.5 TypeScript中的econst divDom = document.getElementById("div");
if (divDom) {
const pay = function(current: number, discount: number): number{
return current * (discount/100);
}
divDom.addEventListener("click", function(this: HTMLElement ,e: MouseEvent){
pay(1000, 20);
});
}在这个例子中不仅用到了函数的类型,还有一个特别有意思的 this 的问题,请看下面代码:divDom.addEventListener("click", function(this: HTMLElement ,e: MouseEvent){
pay(1000, 20);
});
this: HTMLElement其实是非常明确的指明 addEventListener 的 handler 函数的 this 的问题,这个 this 是指向了一个 HTML 元素对象,举个具体的例子,假设我们自己写了一个UI组件,提供了一个 addEventListener ,在处理 handler 函数时,应该将 this 指向这个这个UI组件实例,在 TypeScript 中写法如下:class UI {
public name: string;
constructor(){
this.name = "id";
}
public addEventListener(type: string, handler: (this: UI, e: string) => void) {
///
handler.call(this, this.name);
}
}
const ui = new UI();
const onClick = ui.addEventListener("click", function(e: string){
console.log(this.name);
})
2020你应该知道的TypeScript学习路线【数组类型】
一、概述在JavaScript声明数组时,可以不用指定数组的数据类型;而在TypeScript中,声明数组需要指定数据类型,如string、number等。今天小编就带大家一起来学习下TypeScript的声明,如果大家觉得有帮助,记得给小编点个赞,如果想获取更多干货请关注前端微服务公众号,不定期为大家带来干货。二、使用场景存储多个数据时,声明多个变量就太繁琐了。数组,是用于存放多个数据的集合。有数组:只需要使用一个数组([]),就可以存储任意多个数据。三、TypeScript 声明数组的语法const array_name[:datatype]; //声明
array_name = [val1,val2,valn..] //初始化或者直接在声明时初始化:const array_name[:data type] = [val1,val2…valn]如果数组声明时未设置类型,则会被认为是 any 类型,在初始化时根据第一个元素的类型来推断数组的类型。const numberArr = [1,2,3];四、使用案例4.1 [数据类型 + 方括号] 表示法// 数字类型
const numberArr: number[] = [1, 2, 3, 4];
// 字符串类型
const stringrArr: string[] = ['1', '2', '3'];
//任意类型
const anyArr: any[] = [1, 'Poplar', 'Phoenix’];
//指定数组初始化大小
const arr_names:number[] = new Array(4)
for(let i = 0; i<arr_names.length; i++) {
arr_names[i] = i * 2
console.log(arr_names[i])
}
// 直接初始化数组元素
const sites:string[] = new Array("Google","IE","Taobao","Facebook")
for(let i = 0;i<sites.length;i++) {
console.log(sites[i])
}4.2 数组泛型// 数字类型
let numberArrr: Array<number> = [1, 2, 3, 4];
// 字符串类型
const stringrArr: Array<string> = ['1', '2', '3'];
//任意类型
const anyArr: Array<any> = [1, 'Poplar', 'Phoenix’];4.3 用接口表示数组/**
* 数字类型(NumberArray表示只要inde值类型为number,那么值类型必须为number)
*/
interface NumberArray {
[index: number]: number
}
let numberArrr: NumberArray = [1, 2, 3, 4];
// 字符串类型
interface StringArray {
[index: number]: string
}
const stringrArr: StringArray = ['1', '2', '3'];
//任意类型
interface AnyArray {
[index: number]: any
}
const anyArr: AnyArray> = [1, 'Poplar', 'Phoenix’];4.4 类数组类数组(Array-like Object)不是数组类型,比如 arguments:function sum() {
let args: number[] = arguments;
}
// index.ts(2,7): error TS2322: Type 'IArguments' is not assignable to type 'number[]'.
// Property 'push' is missing in type 'IArguments'.
事实上常见的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:
function sum() {
let args: IArguments = arguments;
}4.5 数组中对象类型的定义const person: { name: string, age: Number }[] = [
{ name: "张三", age: 18 },
{ name: "李四", age: 28 },
];这种形式看起来比较麻烦,而且如果有同样类型的数组,写代码也比较麻烦,TypeScript 为我们准备了一个概念,叫做类型别名(type alias)。比如刚才的代码,就可以定义一个类型别名,定义别名的时候要以type关键字开始。现在定义一个Lady的别名。type Person = { name: string, age: Number };有了这样的类型别名以后,就可以把上面的代码改为下面的形式了。const person:Person [] = [
{ name: "张三", age: 18 },
{ name: "李四", age: 28 },
];这样定义是完全起作用的,但我们扩展属性就会报错,这时候我们就用类来解决。class Person {
name: string;
age: number;
}
const person: Person[] = [
{ name: "张三", age: 18 },
{ name: "李四", age: 28 },
];
2020你应该知道的TypeScript学习路线【联合类型-接口】
一、概述TypeScript中除了es5中的string、boolean、number、array、null和undefined之外还多了元组类型tuple、枚举类型enum、任意类型any、void类型、never类型,除此之外还有一种特殊的类型联合类型,学过Java的小伙伴应该并不陌生接口这个名词,今天小编带着大家认识下Typescript中的接口是什么样的,学完这个是不是都对后端的Java又有了更近一步认识,距离全栈开发又近了一步,如果大家觉得有帮助,记得给小编点个赞,如果想获取更多干货请关注前端微服务公众号,不定期为大家带来干货。二、联合类型(Union Types)联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值(注意:只能赋值指定的类型,如果赋值其它类型就会报错)用一句话概括就是联合类型表示取值可以为多种类型中的一种。三、创建联合类型的语法格式Type1|Type2|Type3 四、使用案例4.1 声明一个联合类型/**
* 联合类型
*/
let type:string|number
type = 12
console.log("数字为 "+ type)
type = "xunzhaotech"
console.log("字符串为 " + type)
//错误代码
type = true
console.log("布尔类型 " + type)4.2 将联合类型作为函数参数使用/**
* 联合类型参数
*/
function getName(name:string|string[]) {
if(typeof name == "string") {
console.log(name)
} else {
let i:number;
for(i = 0;i<name.length;i++) {
console.log(name[i])
}
}
}
getName("xunzhaotech")
console.log("输出数组参数....")
getName(["张三","李四"])4.3 联合类型数组/**
* 联合类型数组
*/
let arr:number[]|string[];
let i:number;
arr = [1,2,4]
console.log("**数字数组**")
for(i = 0;i<arr.length;i++) {
console.log(arr[i])
}
arr = ["张三","李四"]
console.log("**字符串数组**")
for(i = 0;i<arr.length;i++) {
console.log(arr[i])
}4.4 接口关键词interface来定义一个接口interface Person {
name: string
}
function getPerson(person:Person): void {
console.log(person.name);
}
let obj = { age: 10, name: 'xunzhaotech' };
getPerson(obj); // xunzhaotech4.4.1 可选属性interface Person {
name: string;
age?: string;
}
function getPerson(person:Person): void {
console.log(person.name);
}
let obj = { name: 'xunzhaotech' };
getPerson(obj); // xunzhaotech4.4.2 只读属性拥有只读属性不可改变它的值interface Iperson {
readonly name: string;
age: number;
}
let obj: Person = { name: 'zunzhaotech', age: 29 };
obj.name = 'xunzhaotech' // error4.4.3 函数类型interface Person {
(name: string, age: number): boolean;
}
let getPerson:Person = (name, age) => {
return age > 20;
}
getPerson('xunzhaotech', 29)4.4.4 常用的Object定义interface Person {
name: string;
age?: number|string;
}
let list: Person[] = [
{
name: '王麻子'
},
{
name: '张三',
age: 29
},
{
name: '李四',
age: '29'
}
]