注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。
uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/
业务场景描述有这样一个真实的业务场景:在某大厂的订单与支付模块,当有用户下了订单之后,需要在支付模块进行支付,支付动作完成之后,支付模块会将支付完成的结果返回给订单模块来通知用户,该订单是否支付成功,即商品是否已经成功购买了。在微服务分布式架构模式下,上述业务场景中出现了一种异常现象:当用户下了订单之后,在支付模块进行支付时,系统一直没有响应,无论是否成功支付,用户都收不到任何通知信息。程序员在排查对应的业务实现代码时,证实了业务实现代码没有问题,这就导致无法定位问题所在。最终经过几名同事一起排查,发现是订单模块与支付模块之间进行数据传输时,支付模块收到了订单模块传递过来的数据,但是由于服务器高压工作,导致支付模块始终无法处理该支付请求,这就导致系统一直没有响应。问题原因分析在解决问题之前,我们首先来分析一下这种问题产生的原因。在前面我们介绍什么是 Hystrix 资源隔离小节中,我为大家阐述了在我们的 Web 项目中,进程与线程之间的关系。我们知道,在一般情况下,一个 Web 项目中只有一个工作线程来负责处理用户调用的请求和服务,当该工作线程所负责的请求处理缓慢时,该线程就会一直处理当前的请求,导致后续请求只能等待处理,这就是我们说的雪崩现象。雪崩效应产生原理在微服务分布式架构模式下,由于我们没有对线程进行处理,至此在处理所有业务请求时,扔是只有一个工作线程,这就导致上述业务场出现了我们所说的雪崩现象,不过还好,这种雪崩现象比较轻微,只影响到了一个业务模块。很多时候,当我们的项目架构演变为基于微服务的分布式架构时,服务器也需要同步进行更新,有很多企业为了节约成本,则只更新很少数量的服务器,或者压根就不更新服务器,这就导致经常会出现由于服务器高压工作而出现的请求处理缓慢,或请求无法继续处理的情况。
信号量隔离和线程池隔离的方式很相似,只不过把分配线程池的方式改为了分配信号量(至于什么是信号量,请同学们自行查阅)。在处理请求时,Hystrix 会分配一个信号量的阀值,当服务接收到一个请求后,信号量的阀值减 1 ,当请求处理完毕后,信号量的阀值加 1,当信号量的阀值减为 0 时,则不再接收请求,即该请求会被拒绝处理。@RequestMapping(value = "hello", method = RequestMethod.GET)@HystrixCommand(fallbackMethod="helloFail", commandProperties = {@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100")})@ResponseBodypublic String hello() throws InterruptedException { return "helloWorld";}public String helloFail() { return "helloFailed";}代码解释:第 4 行,通过指定 HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY 参数的值为 SEMAPHORE ,来声明该接口使用信号量隔离。第 7 行,通过指定 HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS 参数的值为 100 ,可以理解为设置信号量的阀值为 100 。通过添加上述配置参数,我们就可以通过信号量隔离的方式来实现服务资源隔离。Tips: 一定要合理设置信号量的阀值,不要随意设定,如果阀值设置过大,则请求不会停止,如果阀值设置过小,则不能满足业务需要。
在 Hystrix 中,实现服务资源隔离有两种方式,分别是线程池隔离和信号量隔离。
通过对处理项目中的工作线程的隔离,来避免工作线程处理接口时所产生的阻塞行为,从而保证工作线程可以顺利地调用接口来满足业务需要。而隔离工作线程的方式,就是为每个接口分配一个线程池,并在线程池中维护一定数量的线程,这样,当上述的接口 2 发生服务资源等待时,由于每个接口都分配了不同的线程池,所以不会影响到后续的 3 4 5 接口,如下图所示:线程池隔离实现原理可以看到,由于为每个服务接口均分配了不同的线程池,所以在接口 2 出现服务等待时,并不会影响后续接口的调用,从而保证了业务的顺利进行。我们继续以 hello 方法为例,来看如何实现线程池隔离。@RequestMapping(value = "hello", method = RequestMethod.GET)@HystrixCommand(threadPoolKey = "HelloHystrix", threadPoolProperties = { @HystrixProperty(name = "coresize", value = "2"), @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "true"), @HystrixProperty(name = "maximumSize", value = "2"), @HystrixProperty(name = "maxQueueSize", value = "2")})@ResponseBodypublic String hello() throws InterruptedException { return "helloWorld";}代码解释:第 2 行,我们通过配置 HystrixCommand 注解的 threadPoolKey 属性来为本接口分配一个名称为 HelloHystrix 的线程池。第 3 行,我们通过配置 threadPoolProperties 中的参数属性,来维护 HelloHystrix 线程池中的核心线程数量、最大线程数量。通过添加上述注解并配置其中的属性,我们就可以通过线程池隔离的方式来实现服务资源隔离。Tips: 线程池中的线程数量,一定要根据该接口所实现的业务需求来设置,设置过多,则会浪费资源空间,设置过少,则不能支撑业务需要,所以配置线程数量一定要谨慎。
本节内容概览本小节从服务资源隔离的前提概念开始介绍,采用图文并茂的方式,详细介绍了进程和线程、Web 项目中的进程与线程、服务资源隔离产生的原因,以及服务资源隔离的概念;接着,我们采用代码实现的方式,对 Hystrix 中的线程池隔离和信号量隔离这两种实现服务资源隔离的措施进行了介绍,并对其中需要注意的地方做了补充。服务资源隔离是微服务项目治理中的最后一关,同时也是至关重要的一关,微服务项目经常由于服务没有响应而导致后续服务瘫痪,所以,掌握服务资源隔离是保证微服务项目正常运行的关键所在。
在我们正式介绍什么是服务资源隔离之前,我们先来了解一些前置的概念,这些概念是理解服务资源隔离的前提。进程与线程进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。我们可以把进程理解为我们项目运行的载体,就比如我们乘坐的公交车,公交车相对于我们来说就是一个载体,来承载我们到达不同的目的地,进程就是如此,只不过在进程中被承载的是线程罢了。线程:线程(Thread)是操作系统能够进行运算调度的最小单位。我们可以把线程理解为,执行某一具体的计算机系统任务的执行者,比如在公交车中,负责开公交车的司机师傅就可以被当做一个线程。在一个进程中,可以存在多个线程,即在一辆公交车中可以存在多名乘客,他们分别都去往不同的目的地,但是,一个线程只能属于一个进程,不属于不同的两个进程,即一名乘客同一时刻只能乘坐一辆公交车,不可能在同一时刻乘坐两辆公交车。在理解了什么是进程与线程之后,我们来看一下在我们的 Web 项目中,进程与线程都是怎样存在的,以及他们之间的关系是怎样的。Web 项目中的进程与线程我们的每一个 Web 项目都可以被看作一个进程,且一个 Web 项目只能是一个进程。在我们的 Web 项目中,常规情况下只有两个线程,分别是主线程和工作线程,其中,主线程负责我们的项目启动以及一些项目初始化工作,而工作线程则主要负责项目中的请求处理与业务逻辑执行,项目中进程与线程的关系如下图所示:Java Web 项目中进程与线程的关系根据上图,我们可以这样理解:一个 Web 项目在计算机系统中就是一个进程,而在这个进程中,存在一个主线程和一个工作线程,并且主线程主要负责项目启动,而工作线程主要负责请求的处理。在理解了这个关系之后,让我们来看一下什么是服务资源隔离。服务资源隔离在介绍服务资源隔离之前,我们需要先了解什么是服务资源。服务资源一般来讲,指的是项目正常运行所需要的基础环境、基础设施、静态资源文件等内容,而对于 Web 项目来说,其项目本身即是一种服务资源,服务调用者通过调用项目提供的服务来满足他们的业务需求。而对于服务提供者来说,这些业务需求的实现在项目中一般就是我们所开发的接口,所以,在项目中所实现的业务接口即是我们这里所说的服务资源。那么,为什么需要把服务资源进行隔离呢?我们知道,正常情况下,在 Web 项目中只有一个工作线程,且这个工作线程负责接口请求的处理。在正常应用场景下,服务调用者会调用我们项目所提供的接口来满足业务需求,这里假设我们的一个 Web 项目中具有 5 个接口,服务调用者会根据业务顺序来调用我们的接口,如果一切顺利,则业务即可正常顺利地进行下去。但是,如果服务调用者在调用接口时,其中一个接口所需要处理的业务比较复杂,导致这个接口不能及时的结束,这就导致我们后续的接口调用只能等待,直到该接口处理完毕后才能继续向下执行,如果该接口一直不能处理完毕,则后续接口就会一直等待,从而影响业务的正常开展,这种现象就被称为服务资源等待,如下图所示:服务资源等待产生原理我们可以把上图中的工作线程访问理解为服务调用者,在服务调用者调用接口 2 时,由于接口 2 迟迟不能处理,导致接口 2 出现服务等待,并最终影响后续的接口 3、接口 4、接口 5 的调用,从而影响了业务的顺利进行。如果通过采取某种措施,使满足同一业务需求的不同服务资源间进行隔离,来有效缓解或解决服务资源等待问题,那么业务就可以正常顺利地开展下去,所以人们就提出了服务资源隔离的概念。
技巧 1如果我们的微服务监控平台没有任何数据,或者说,在打开微服务平台之后,各参数一直处于 loading 状态,这个时候,我们只需要在服务端调用任意一个服务接口即可,这样在微服务监控平台,我们就能看到被监控实例的参数了。技巧 2如果我们在访问 /actuator/hystrix.stream 路径时,系统找不到对应的路径,即报 404 异常,那么我们需要在对应项目的启动类中添加一个 Bean :@Beanpublic ServletRegistrationBean hystrixMetricsStreamServlet() { ServletRegistrationBean registration = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); registration.addUrlMappings("/hystrix.stream"); return registration;}这样我们就能正常访问 /actuator/hystrix.stream 下的路径了。
微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。
整个 Hystrix 微服务监控平台分为平台首页和平台内容页,在平台首页,我们需要配置被监控项目的信息,包括被监控项目的地址、获取被监控项目参数的间隔时间,以及服务监控平台的标题。在平台内容页才是我们真正查看被监控项目之参数的地方,接下来就让我们先看平台首页中都有哪些内容。平台首页Hystrix 微服务监控平台登录界面如下图所示:Hystrix Dashboard 登录界面我们按照图中字母顺序依次进行介绍。1 表示需要进行监控的服务访问地址。2 表示获取服务监控信息的刷新频率,即间隔多长时间之后重新获取被监控服务的信息。3 表示进入微服务监控平台之后,所展示的标题名称,往往这里会被设置为项目的名称。4 表示进入微服务监控平台的按钮,当我们将 1 2 3 的信息都填写正确之后,点击 4 按钮,即可进入微服务监控平台内容页面,如果 1 2 3 信息中有一项填写错误,则在微服务监控平台的内容页面就无法看到任何服务监控信息。Tips: 上面的 1 2 3 项中,只有 1 是必填项,如果 2 3 没有填写,则 Hystrix 会启用默认的配置进入微服务监控平台。平台首页一共就需要我们了解以上这些信息就可以了,接着我们来看平台内容页。平台内容页Hystrix Dashboard 内容页面整个平台内容页面被分为了 Circuit 和 Thread Pools 两部分,分别表示项目熔断的监控,以及项目线程池的监控。我们首先来看平台内容界面的 Circuit 部分。上图中,在 Circuit 部分的右上角位置,我用箭头标出了每个名词的对应关系,接下来我们一一来说明。success : 表示请求服务成功的数量。short-circuited : 表示请求服务被熔断的请求数量。bad request : 表示不能访问的请求的数量。timeout : 表示请求服务时,服务超时的请求数量。rejected :表示请求服务时,被拒绝的请求数量。failure :表示请求服务时,请求失败的数量。error :表示请求服务时,错误的服务的数量。然后,我们接着来看,在 Circuit 部分的中间位置,有一个 Circuit 的单词,该单词表示被监控项目此时的断路器的状态,当 Circuit 的值为 Closed 时,表示当前项目的断路器处于关闭状态,即当前服务的项目熔断没有启用。当项目的断路器打开时,此时的 Circuit 的值为 Open ,表示,此刻当前被监控的项目使用了服务熔断机制。在 Circuit 单词的上方,还有两个重要的参数,分别是:Host、Cluster ,Host 表示当前被监控项目的主机的请求频率,Cluster 表示当前被监控项目的项目集群的请求频率。Circuit 部分的左上方是一个图标,其由实心圆和曲线组成,其中,实心圆会根据实例的健康程度来改变自身的颜色,健康程度从绿色、黄色、橙色、红色递减,而实心圆的大小则表示请求流量的变化,流量越大该实心圆就越大,所以可以在大量的实例中快速发现故障实例和高压实例。曲线则记录了在 2 分钟内请求流量的相对变化程度,可以通过它来观察流量的上升和下降趋势。Tips: 由于本项目只是用来演示,并没有请求流量产生,所以这里就不会出现曲线,当我们有请求过来时,曲线就会发生变化。Circuit 部分最下面的 Hosts 、Median 、 Mean 我们不需要可以去了解分别代表什么含义,我们只需要知道这三个参数都是用来表示延迟时间的就可以了。最后,我们来看平台内容页的 Thread Pools 部分。在 Thread Pools 部分的左上方,同样也有一个由实心圆和曲线所构成的图标,其含义同 Circuit 部分的图标,这里不再赘述。在 Thread Pools 图标的右侧,同样也有 Host 和 Cluster 的请求频率的记录,这个和 Circuit 部分也是一样的。在 Thread Pools 图标的下方,有两列非常重要的参数,如下图:Hystrix Dashboard-Thread Pools 重点监控属性Active : 表示当前活跃的线程数量。Max Active : 表示当前最大活跃线程数量。Queued : 表示当前线程等待队列中的线程数量。Executios : 表示正在执行的线程的数量。Pool Size : 表示当前线程池的大小。Queue Size : 表示当前线程等待队列的长度。最后,我们再来看一下 Circuit 部分和 Thread Pools 部分的最上方位置,均有一个 Sort 名词,在这个名词的后面,也有很多单词,这表示我们查看服务监控参数时的顺序,当我们点击后面的一个单词时,HystrixDashboard 就会根据我们所点击的单词来对服务监控参数进行一个排序,仅此而已。Tips: Thread Pools 部分可以为我们展示被监控项目中线程池的活跃状态,我们可以直接从这些参数中分析出来当前线程池的活跃程度,从而动态地去调整每个微服务项目的线程池内容。
首先我们来测试服务下线的情况,服务提供者下线分为两种情况,一种是服务出现故障,与 Zookeeper 服务端断开连接时,另一种是业务需求手动对服务进行下线处理。服务停机我们先来测试服务停机时的情况,这里我们手动关闭端口为 8092 的服务,模拟服务停机,等待会话超时,Zookeeper 服务端就会移除会话失效的临时节点,然后再次访问服务消费者的接口 http://localhost:9090/consumer/callMethod :调用了服务提供者 192.168.0.102:8090 的方法刷新页面,再次访问:调用了服务提供者 192.168.0.102:8091 的方法再次刷新页面进行访问:调用了服务提供者 192.168.0.102:8090 的方法我们发现端口为 8092 的服务已经无法被访问了,但是服务消费者并没有发生异常,说明服务下线成功。这里我们还可以在节点监听的回掉方法中发送短信或邮件通知系统管理员,提醒他们服务下线了。接下来我们测试手动把服务提供者下线的情况。手动下线如果某个服务需要手动下线,我们就可以访问我们在服务提供者中提供的下线方法,这里我们把端口为 8091 的服务下线,访问 http://localhost:8091/provider/offline ,查看浏览器:>>> 服务提供者 192.168.0.102:8091 已下线我们发现端口为 8091 的服务已经下线了,手动下线的情况我们不需要等待会话超时,因为这个会话还存活着,接下来我们就可以再次访问服务消费者的接口 http://localhost:9090/consumer/callMethod :调用了服务提供者 192.168.0.102:8090 的方法我们发现现在只有端口为 8090 的服务能提供服务了,说明服务下线成功了。测试完服务下线的情况,我们来对服务进行上线测试,上线也同样分为两种情况,一种是停机的服务重新启动成功时会注册自身的地址到 Zookeeper 服务,另一种是把手动下线的服务手动进行上线,手动上线的过程同样是注册自身的地址信息到 Zookeeper 服务,接下来我们就同时测试上面两种服务上线的情况。服务启动 + 手动上线首先我们启动上面停机的 8092 服务,然后调用端口为 8091 的手动上线方法 http://localhost:8091/provider/online ,执行完成后,我们就可以使用服务消费着来进行测试了,调用服务消费者的接口 http://localhost:9090/consumer/callMethod 查看浏览器内容:调用了服务提供者 192.168.0.102:8090 的方法刷新页面,再次访问:调用了服务提供者 192.168.0.102:8091 的方法再次刷新页面进行访问:调用了服务提供者 192.168.0.102:8092 的方法我们可以发现,服务消费者依次调用了 8090 ,8091 ,8092 这 3 个服务提供者的方法,并且实现了轮询的负载均衡策略,说明我们的服务上线成功
1. 独立的页面维护了全局 错误码错误码由5位整数构成2. 每个接口一个独立的 参数说明页面正常情况下出参只返回业务实体异常情况才有 errCode errMsg每个接口下也可能有自己的业务错误码
当用户打开一个 App 时,Android 系统会创建一个 Linux 进程,同时在进程中创建一个执行线程,我们称之为“主线程”,因为 Andfoid 规定只能在主线程更新 UI,所以又叫“UI线程”。系统在创建主线程的时候帮我们创建好了一套消息处理机制,包含了第 38 节提到的 Handler、Looper、MessageQueue 等模块,主线程就利用这一套消息机制来实现 Actvity、Fragment 的生命周期回调以及和其他 App 之间的通信。所有需要在 UI 线程执行的任务都要首先被 push 到任务队列中,然后等待主线程 Looper 来轮询。如果我们将所有的任务都放到主线程的任务队列,那么可能需要等很久才能执行到,所以一个比较好的选择就是将耗时任务单独放到一个子线程中,这样就可以独享一个 MessageQuene,并且不再占用主线程的资源。
实现思路分析鉴于上述业务场景中所描述的问题,我们只要为每个业务模块分配不同的工作线程,使各模块间不再一同共用一个工作线程,就可以有效解决上述问题。当我们通过技术手段为每一个业务模块都分配了不同的工作线程之后,各模块的业务处理操作都会有专门的工作线程来完成,不会再出现各模块共用一个工作线程的情况。如果一个模块中的请求处理出现问题而等待,由于我们分配了不同的工作线程,所以这种情况不会影响到其他模块,这就解决了上述业务场景中出现的问题。Hystrix 提供了通过线程池或信号量隔离的方式来对服务资源进行隔离,以解决雪崩现象的发生,接下来让我们分别来看一下代码实现。实操以线程池隔离为例,我们先给订单服务配置资源隔离:@RequestMapping("make_order.do")@ResponseBody@HystrixCommand(threadPoolKey = "userMakeOrderThread")public CommonResponse<String> makeOrder(@RequestBody("order")Order order, @RequestBody("user")User user){ return orderService.makeOrder(order, user);}代码解释第 3 行,我们用户下订单服务中通过添加 HystrixCommand 注解的 threadPoolKey 属性来为用户下订单服务单独分配了一个名为 userMakeOrderThread 的线程池,当我们再有用户下订单的请求需要处理时,就会使用这个线程池中的线程。同样的方法,我们为用户支付服务配置资源隔离:@RequestMapping("aliPay.do")@ResponseBody@HystrixCommand(threadPoolKey = "userAliPayThread")public CommonResponse<String> aliPay(@RequestBody("shipping")Shipping shipping, @RequestBody("user")User user){ return payService.aliPay(shipping, user);} 可以看到,我们为支付模块分配了一个名为 userAliPayThread 的线程池。基于信号量隔离的服务隔离的配置,和上述基于线程池隔离的配置大同小异,下面我将关键代码放到下方,各位同学只需要替换掉上述的线程池配置即可。以用户下订单服务为例:@HystrixCommand(fallbackMethod="makeOrderFailed", commandProperties = {@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "50")})代码解释第 3- 8 行,使用信号量隔离需要显式声明服务资源隔离策略,SEMAPHORE 表示使用信号量隔离策略。Tips: 1. 在实际项目开发中,像这种订单服务和支付服务互相依赖的业务场景不在少数,各位同学在工作时,一定要在这种业务场景中配置好服务资源隔离,不要等在线上出现问题之后再解决,那就晚了; 2. 线程池隔离和信号量隔离之间的区别有很多,这里就不做介绍了,希望各位同学可以自行查阅,他们之间的区别可以帮助你确定不同业务场景下采用哪种隔离方式比较合适。
在前面的章节有提到过,Android 系统默认会在主线程(UI 线程)执行任务,但是如果有耗时程序就会阻塞 UI 线程,导致页面卡顿。这时候我们通常会将耗时任务放在独立的线程,然后通过 Handler 等线程间通信机制完成 UI 的刷新。很多时候我们也许只是想执行一个简单的任务,为此写一套 Handler 线程通信就会显得比较复杂,不用担心,Android 系统为我们提供了一个专门用于执行异步任务的工具——Async Task,它可以让我们执行后台任务并轻松的与 UI 线程进行状态同步,今天就一起来学习一下 AyncTask 的用法。
Hystrix 中的服务监控平台,就是对微服务项目进行监控的平台,包括服务运行的状态、服务有无宕机、服务异常信息监控等内容,为开发者和运维者提供了比较友好地界面支持,开发者和运维者可以直接通过观察服务平台界面,来判断具体微服务的状态信息,从而更好地对微服务进行控制。默认的 Hystrix 中间件已经为我们封装好了微服务监控平台,但是需要通过引入依赖的方式来使用它,接下来就让我们来看一下如何搭建该服务监控平台吧。
服务熔断机制的服务快速失败所实现的目标,和我们在上述小节中所介绍的普通的服务快速失败所实现的目标相同,都是为了保证,在某一微服务发生故障时,不影响后续微服务的正常运行。雪崩效应产生原理在上述小节中,各位同学已经对什么是雪崩效应有所了解,如上图所示,我们所讲的服务熔断也是为了避免和解决雪崩现象的发生,只不过所采用的手段不同而已,那么接下来,就让我们来看一下基于服务熔断机制的服务快速失败的概念是什么吧。针对服务熔断机制,我们先来介绍什么是熔断?熔断这一名词,其实不是来源于计算机相关专业,而是来源于电子工程相关专业。熔断的核心是断路器,对于断路器来说,我们可以将断路器理解为一根保险丝,在日常生活中,当我们家庭用电超过负载时,保险丝就会迅速烧断,阻止由于电流过大而烧毁整个家庭电路。同样地,熔断我们也可以像保险丝那样理解,即在计算机相关领域中,由于中断上游的故障服务,而保全整体的服务的措施就被称为熔断,而实现中断上游的故障服务所采取的核心措施就是我们的断路器。服务熔断机制就是把这些概念都统一起来,然后封装到 Hystrix 中,且最终应用于我们的微服务项目中间,通过配置断路器,来保全我们整体的微服务项目,这就是 Hystrix 所提供的服务熔断机制。Tips: 1. 在实际工作中,我们需要灵活的去配置微服务项目中,各个领域的微服务所对应的断路器配置,包括间隔时间、持续时间等关键属性,切记不要凭感觉去配置; 2. Hystrix 本身所提供的服务熔断机制并不是很好用,往往需要我们在项目中集成其他的微服务服务中间件来一起集成使用,单独使用 Hystrix 服务熔断机制的项目很少见。
在介绍服务快速失败之前,我们需要首先了解一个概念,那就是雪崩效应,那么什么是雪崩效应呢?雪崩效应,是一种微服务项目间出现的一种不良现象,一般是指:由一个微服务发生故障之后,影响到了其他微服务的正常运行,或者说,当一个微服务发生故障不能正常运行时,所导致的其他微服务也跟着受影响,导致其他的微服务也不能正常运行。假设现在有 4 个微服务,分别用服务 A 、服务 B 、服务 C 、服务 D 表示,并且,服务 B 的运行依赖于服务 A ,服务 C 的运行依赖于服务 B ,服务 D 的运行也依赖于服务 B , 这 4 个微服务之间的正常运行关系如下图所示:假想微服务架构为了验证雪崩效应,现在,我们假设服务 B 由于外界因素发生了宕机现象,那么此时的服务 B 由于没有任何保护措施,所以服务 B 无法正常运行。由于服务 B 不能正常运行,所以,导致依赖于服务 B 的服务 C 和服务 D 都不能继续正常运行了,如下图所示:雪崩效应产生原理Tips: 1. 在实际工作中,在正式开发项目之前,应该就可能会出现雪崩现象的因素展开集中讨论,并将讨论结果以书面文件形式进行存储,这样一来,在真正发生雪崩现象时,可以及时启用应急方案; 2. 在开发微服务项目时,我们应该尽最大可能的去避免雪崩现象的发生,因为雪崩现象除了会影响业务的正常开展之外,也会影响我们的资源消耗。在了解了什么是雪崩效应之后,接下来我们来看一下什么是服务快速失败。服务快速失败,就微服务而言,是指:当微服务之间发生雪崩现象时,在程序中通过采用某种技术方法或手段,来将引起雪崩效应的微服务快速处理,其处理的最终目的是要保证该微服务不会导致其他后续的微服务出现故障。说白了,服务快速失败就是当微服务发生故障时,所采取的一个兜底的方案,该方案的执行会迅速终止发生故障的微服务的继续运行,且可以保证后续微服务的正常运行,具体如下图所示:服务快速失败的作用在上图中,我们可以看到,发生故障的服务 B,由于采用了服务快速失败机制,导致在发生故障时采取了快速失败措施,且同时也保证了服务 C 和服务 D 的正常运行(至于如何保证的服务 C 和服务 D 的正常运行,就涉及到了源码层面,由于该知识不符合本套课程的初衷,所以这里不予介绍)。Tips: 1. 服务快速失败几乎称为了每个微服务项目中必不可少的措施,所以,我们在开发微服务项目时,一定要采用服务快速失败机制,来保证我们项目的正常运行; 2. 理解什么是雪崩效应是理解什么是服务快速失败的前提概念,同学们一定要对这两个概念有所了解才行。
首先给出进程和线程的基本定义:进程(Process) :是操作系统任务调度的基本单元, 目的是为了实现操作系统的并发。线程(Thread) :线程是进程的子任务,是进程中实际运行的任务,线程是程序执行的最小单元。然后分析两者之间的主要区别:(1)包含关系:一个线程肯定归属于一个进程,但是一个进程可以包含多个线程。(2)内存管理:操作系统会给进程分配独立的内存空间,但是一个进程下的多个线程共享内存空间。以 Java 编程为例,同一个 main 函数进程下的多个线程共享代码段(代码和常量),以及数据段(全局变量和静态变量),这些都是共享的内存空间,不过需要注意,每个线程会有独立的运行栈空间。(3)单元定义:从内存分配的角度就能看出,进程是资源分配的最小单元,线程是 CPU 执行的最小单元。(4)系统开销:创建进程和线程的系统开销是不同的,因为在创建和销毁进程时,操作系统需要分配和回收内存资源,创建线程不需要切换整体内存空间,所以创建进程的系统开销远大于创建线程;在切换进程时需要保存 CPU 运行的上下文,切换线程只需要切换 CPU 中少数寄存器的内容,所以切换进程的系统开销也远大于切换线程。(5)稳定性分析:因为不会共用内存空间,所以一个进程挂了对另外的进程影响很小,但是同一进程下的线程是共享内存的,所以一个线程挂了,会影响到其他线程。(6)通信:因为不同进程处于不同的内存空间,所以通信方式比较麻烦,具体方式将在之后的小节介绍。同一进程下的线程之间通信方式相对简单,因为共享内存,可以读写相同的内存空间。
我们只需要通过配置文件指定数据源信息, Spring Boot 就可以识别配置,并加载到数据源组件中。 JdbcTemplate 也可以自动识别该数据源,从而实现对数据库的操作。配置文件信息如下:实例:# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=123456需要注意的是,我们在 URL 配置中指定了编码方式,这样可以防止出现数据库中文乱码情况。同时指定了时区为北京时间所在的东八区(GMT%2B8),避免因时区问题导致错误。此时再次启动 Spring Boot 应用,正常运行,说明我们的数据源配置生效了。
Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。
下图展示了每线程模型的结构。从图中可以看出,每线程模型的程序结构如下:创建一个监听线程,通常会采用 Java 主线程作为监听线程。创建一个 java.net.ServerSocket 实例,调用它的 accept 方法等待客户端的连接。当有新的客户端和服务器建立连接,accept 方法会返回,创建一个新的线程和客户端通信。此时监听线程返回,继续调用 accept 方法,等待新的客户端连接。在新线程中调用 java.net.Socket 的 recv 和 send 方法和客户端进行数据收发。当数据收发完成后,调用 java.net.Socket 的 close 方法关闭连接,同时线程退出。下来,我们通过一个简单的示例程序演示一下每线程模型服务器的编写方法。示例程序的基本功能如下:客户端每隔 1 秒向服务器发送一个消息。服务器收到客户端的消息后,向客户端发送一个响应消息。客户端发送完 10 个消息后,关闭 Socket 连接,程序退出。服务器检测到客户端关闭连接后,同样关闭 Socket 连接,并且负责和客户端通信的线程也退出。客户端代码:import java.io.*;import java.net.InetSocketAddress;import java.net.Socket;import java.net.SocketAddress;public class TCPClientMultiThread { // 服务器监听的端口号 private static final int PORT = 56002; // 连接超时时间 private static final int TIMEOUT = 15000; // 客户端执行次数 private static final int TEST_TIMES = 10; public static void main(String[] args) { Socket client = null; try { // 测试次数 int testCount = 0; // 调用无参构造方法 client = new Socket(); // 构造服务器地址结构 SocketAddress serverAddr = new InetSocketAddress("192.168.0.101", PORT); // 连接服务器,超时时间是 15 毫秒 client.connect(serverAddr, TIMEOUT); System.out.println("Client start:" + client.getLocalSocketAddress().toString()); while (true) { // 向服务器发送数据 DataOutputStream out = new DataOutputStream( new BufferedOutputStream(client.getOutputStream())); String req = "Hello Server!"; out.writeInt(req.getBytes().length); out.write(req.getBytes()); // 不能忘记 flush 方法的调用 out.flush(); System.out.println("Send to server:" + req); // 接收服务器的数据 DataInputStream in = new DataInputStream( new BufferedInputStream(client.getInputStream())); int msgLen = in.readInt(); byte[] inMessage = new byte[msgLen]; in.read(inMessage); System.out.println("Recv from server:" + new String(inMessage)); // 如果执行次数已经达到上限,结束测试。 if (++testCount >= TEST_TIMES) { break; } // 等待 1 秒然后再执行 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } finally { if (client != null){ try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } }}服务器代码:import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class TCPServerPerThread implements Runnable{ private static final int PORT =56002; private Socket sock = null; TCPServerPerThread(Socket sock){ this.sock = sock; } @Override public void run() { // 读取客户端数据 try { while (true){ // 读取客户端数据 DataInputStream in = new DataInputStream( new BufferedInputStream(sock.getInputStream())); int msgLen = in.readInt(); byte[] inMessage = new byte[msgLen]; in.read(inMessage); System.out.println("Recv from client:" + new String(inMessage) + "length:" + msgLen); // 向客户端发送数据 String rsp = "Hello Client!\n"; DataOutputStream out = new DataOutputStream( new BufferedOutputStream(sock.getOutputStream())); out.writeInt(rsp.getBytes().length); out.write(rsp.getBytes()); out.flush(); System.out.println("Send to client:" + rsp + " length:" + rsp.getBytes().length); } } catch (IOException e) { e.printStackTrace(); } finally { if (sock != null){ try { sock.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { ServerSocket ss = null; try { // 创建一个服务器 Socket ss = new ServerSocket(PORT); while (true){ // 监听新的连接请求 Socket conn = ss.accept(); System.out.println("Accept a new connection:" + conn.getRemoteSocketAddress().toString()); Thread t = new Thread(new TCPServerPerThread(conn)); t.start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } }}客户端采用单线程模型。服务器采用每线程模型,我们采用实现 Runnable 接口的方式实现多线程逻辑。从示例代码可以看出,每线程模型的优点就是结构简单,相比单线程模型,也没有增加复杂度。缺点就是针对每个客户端都创建线程,当和客户端通信结束后,线程要退出。频繁的创建、销毁线程,对系统的资源消耗比较大,只能用在简单的业务场景下。
Java 虚拟机支持多线程并发编程,并发意味着同时执行多个任务。在 Android 中常见的多线程常见就是在子线程执行耗时操作,然后将结果通过线程间通信传递给主线程,主线程仅仅拿到结果进行 UI 的刷新。
通过配置文件,设置数据源信息。由于我们不再使用默认数据源,所以此处需要指定数据源类型为 DruidDataSource 。实例:# 指定数据源类型spring.datasource.type=com.alibaba.druid.pool.DruidDataSource# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=Easy@0122
我们首先来看看,三者之间的关系,从图中可以看到,操作系统是包含多个进程的容器,而每个进程又是容纳多个线程的容器。什么是进程?官方定义: 进程(baiProcess)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。Tips:系统进行资源分配和调度的基本单位其实就是 CPU 时间片的切换,一个 CPU 同一时间只能操作一个任务,只不过 CPU 在不停的切换工作任务,这里的时间片就是我们所说的系统进行资源分配和调度的基本单位。那么从定义上感觉非常的抽象,但是进程其实就在我们日常的计算机使用过程中。请看下图,进入任务管理器看 Windows 操作系统下的进程:什么是线程?官方定义: 线程是操作系统能够进行资源调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,每个线程执行的都是进程代码的某个片段,特定的线程总是在执行特定的任务。线程与进程的区别?诞生起源:先有进程,后有线程。进程由于资源利用率、公平性和便利性诞生。处理器的速度往往比外设的速度快(键盘、鼠标等),为了提高 CPU 的利用率,诞生了线程,目的就是为了提高程序的执行效率;概念:进程是资源分配的最小单位。 线程是程序执行的最小单位(线程是操作系统能够进行资源调度的最小单位,同个进程中的线程也可以被同时调度到多个 CPU 上运行),线程也被称为轻量级进程;内存共享:默认情况下,进程的内存无法与其他进程共享(进程间通信通过 IPC 进行)。 线程共享由操作系统分配给其父进程的内存块。
概述:在多线程编程中,线程个数一般都大于 CPU 个数,而每个 CPU 同一时-刻只能被一个线程使用,为了让用户感觉多个线程是在同时执行的, CPU 资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。定义:当前线程使用完时间片后,就会处于就绪状态并让出 CPU,让其他线程占用,这就是上下文切换,从当前线程的上下文切换到了其他线程。问题点解析:那么就有一个问题,让出 CPU 的线程等下次轮到自己占有 CPU 时如何知道自己之前运行到哪里了?所以在切换线程上下文时需要保存当前线程的执行现场, 当再次执行时根据保存的执行现场信息恢复执行现场。线程上下文切换时机: 当前线程的 CPU 时间片使用完或者是当前线程被其他线程中断时,当前线程就会释放执行权。那么此时执行权就会被切换给其他的线程进行任务的执行,一个线程释放,另外一个线程获取,就是我们所说的上下文切换时机。
本章介绍一下 HTML5 新增的离线存储特性 Localstorage,主要包括 Localstorage 的发展史、兼容性、优缺点以及使用场景。说到 Localstorage,为什么要使用 Localstorage 呢?因为开发程序当然就要存储数据了,但是 Web 开发场景比较特殊,数据正常情况下是要通过 HTTP 协议发送给服务器端,存储在服务器上,但是呢,如果所有数据都存到服务器一方面会浪费服务器资源,另一方面也会降低网页响应速度,所以设计网页时会抽取一些不太重要的或者临时性的数据使用离线存储方式放在浏览器上。总的来说,Localstorage 是一个简单的离线存储技术,通过官方提供的增删改查的 API 可以方便的操作它,需要考虑的难点是每个浏览器的容量限制,使用时做好容错即可。
在 1998 年,IBM 公司成立了一个项目开发小组,来进行开发工具技术的探索,成立该小组的主要原因是 IBM 对于自己的 Java 开发工具 Visual Age for Java 感到非常不满,认为它的底层技术很没有发展空间。2000 年,IBM 推出了 Eclipse,出于商业目的(当时 IBM 需要一个项目来对抗微软的 Visual Studio 的发展),IBM 允许 IBM 以外的人员免费使用 Eclipse,慢慢地,Eclipse 开始在社区流传起来了。2001 年 11 月,IBM 把 Eclipse 贡献给了开源社区,并正式将 Eclipse 项目独立出来,由 IBM 牵头,项目由一些软件开发商的财团支持。2004 年 1 月,IBM 决定成立 Eclipse 基金会(Eclipse Foundation),创立基金会的原因是 IBM 觉得 Eclipse 的发展需要建立在一个与开发商财团无关且透明的开发社区之上。Eclipse 基金会成立后,Eclipse 开始在开发社区飞速地发展起来了。Eclipse 基金会对于 IBM 的描述
咱们打开新浪微博,然后随便找个带皇冠的,按下F12键(Mac用户按command+option+i)然后选中控制台的箭头,再点击皇冠:再点开这个图片可以发现:可以看到那些大 V 图标皇冠图标以及各种微博认证等图标,都是放在了一张雪碧图里(即使再牛的大 V,身份标志也是放在雪碧图中的)。