Earyant的技术博客

欢迎来到Earyant的技术博客,在这里我将与你分享新技术。

看这篇文章

和B+树很像,不过B+树插入需要Rebalance进行树重调整。

看这篇文章讲的很好

B - 树(Balance Tree)

二叉查找树的时间复杂度是O(log(n)) 已经够快了,但是二叉查找树的查找速度取决于树的高度。
B - 树,每个节点包含最多k个孩子,k被称为阶,k的大小取决于磁盘页的大小。

一个 m 阶的 B 树具有如下几个特征

  • 根节点至少有两个子女。
  • 每个中间节点包含k-1个元素和k个孩子,其中m/2 <= k <= m
  • 每个叶子节点都包含k-1个元素,其中 m/2 <= k <=m
  • 所有叶子节点都位于同一层
  • 每个节点中的元素从小到大排列,节点当中 k-1 个元素正好是 k 个孩子包含的元素的值域分划。

B+树

在b-树基础上进行改造,将索引全部建在叶子节点上,非叶子节点指向叶子节点的大小。

看这篇文章

最先想到的当然是for循环了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {
long time1 = System.currentTimeMillis();
System.out.println("当前时间为"+time1);
int n = 0;
int n1, n5, n10, n50;
for (n1 = 0; n1 < 100; n1++) {
for (n5 = 0; n5 < 20; n5++) {
for (n10 = 0; n10 < 10; n10++) {
for (n50 = 0; n50 < 2; n50++) {
if (n1 * 1 + n5 * 5 + n10 * 10 + n50 * 50 == 100) {
n++;
System.out.println("1块的:" + n1 + "张 5块的: " + n5 + "张 10块的 :" + n10 + "张 50块的:" + n50 + "张");
}
}
}
}
}
long time2 = System.currentTimeMillis();
System.out.println("结束时间为"+time2);
long time = time2 - time1;
System.out.println(n + " 耗费时间为 " + time);
}
很容易得出结果154.但是耗费时间为 1501038954420 - 1501038954413 =7;
虽然时间耗费不是很多,但是通过打印信息可以看出来,50为2的时候只有一种情况,却空跑了很多空循环。

在微服务架构中,需要几个关键的组件,服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个组件可以组建一个简单的微服务架构,如下图:

image

注意:A 服务和 B 服务是可以相互调用的,作图的时候忘记了。并且配置服务也是注册到服务注册中心的。

客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul 集群),然后再到具体的服务,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理(下一篇文章讲述),配置服务的配置文件放在 Git 仓库,方便开发人员随时改配置。

一、Zuul 简介

Zuul 的主要功能是路由和过滤器。路由功能是微服务的一部分,比如/api/user 映射到 user 服务,/api/shop 映射到 shop 服务。zuul 实现了负载均衡。

zuul 有以下功能:

Authentication
Insights
Stress Testing
Canary Testing
Dynamic Routing
Service Migration
Load Shedding
Security
Static Response handling
Active/Active traffic management

二、准备工作

继续使用上一节的工程。在原有的工程上,创建一个新的工程。

三、创建 service-zuul 工程

 <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

在其入口 applicaton 类加上注解 @EnableZuulProxy,开启 zuul:

filterType:返回一个字符串代表过滤器的类型,在 zuul 中定义了四种不同生命周期的过滤器类型,具体如下:
pre:路由之前
routing:路由之时
post: 路由之后
error:发送错误调用
filterOrder:过滤的顺序
shouldFilter:这里可以写逻辑判断,是否要过滤,本文 true, 永远过滤。
run:过滤器的具体逻辑。可用很复杂,包括查 sql,nosql 去判断该请求到底有没有权限访问。

在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的 100% 可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务 “雪崩”。
为了解决这个问题,就出现断路器模型。

断路器简介

  • Netflix 已经创建了一个名为 Hystrix 的库来实现断路器模式。 在微服务架构中,多层服务调用是非常常见的。
  • 较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用达到一个阀值(hystric 是 5 秒 20 次) 断路器将会被打开。
  • 断路打开后,可用避免连锁故障,fallback 方法可以直接返回一个固定值。

在 ribbon 使用断路器

  • 改造 serice-ribbon 工程的代码:

    在 pox.xml 文件中加入:

    org.springframework.cloud spring-cloud-starter-hystrix
  • 在程序的入口类加 @EnableHystrix:

  • 改造 HelloService 类,加上 @HystrixCommand,并指定 fallbackMethod 方法。

Feign 中使用断路器

  • 如果你使用了 feign,feign 是自带断路器的,并且是已经打开了。如果使用 feign 不想用断路器的话,可以在配置文件中关闭它,配置如下:

    feign.hystrix.enabled=false

Circuit Breaker: Hystrix Dashboard (断路器:hystrix 仪表盘)

  • 基于 service-ribbon 改造下:

    pom.xml 加入:

    org.springframework.boot spring-boot-starter-actuator
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>        
    

在主程序入口中加入 @EnableHystrixDashboard 注解,开启 hystrixDashboard:

  该仪表盘可以查看错误率

使用feign

spring-cloud-starter-eureka
spring-cloud-starter-feign
spring-boot-starter-web
spring-boot-starter-test

配置文件

eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8765
spring:
application:
name: service-feign

开启feign

  • 在程序的入口类,需要通过注解 @EnableFeignClients 来开启 feign:

    调用服务

  • 定义一个 feign 接口类, 通过 @ FeignClient(“服务名”),来指定调用哪个服务:

在上一篇文章,讲了服务的注册和发现。在服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于 http restful 的。spring cloud 有两种调用方式,一种是 ribbon+restTemplate,另一种是 feign。在这一篇文章首先讲解下基于 ribbon+rest。

ribbon

  • 是一个负载均衡客户端,可以很好的控制 http 和 tcp 的一些行为。Feign 也用到 ribbon,当你使用 @ FeignClient,ribbon 自动被应用。

  • ribbon 已经默认实现了这些配置 bean:

    • IClientConfig ribbonClientConfig: DefaultClientConfigImpl

    • IRule ribbonRule: ZoneAvoidanceRule

    • IPing ribbonPing: NoOpPing

    • ServerList ribbonServerList: ConfigurationBasedServerList

    • ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter

    • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer

准备工作

  • 基于上一节的工程,启动 eureka-server 工程;启动 service-hi 工程,它的端口为 8762;将 service-hi 的配置文件的端口改为 8763, 并启动它,这时你会发现:service-hi 在 eureka-server 注册了 2 个,这就相当于一个小的集群。访问 localhost:8761 如图所示:

新建一个service-ribbon

  • spring-cloud-starter-eureka
  • spring-cloud-starter-ribbon
  • spring-boot-starter-web
  • spring-boot-starter-test

想eureka注册一个客户端

eureka:
   client:
     serviceUrl:
       defaultZone: http://localhost:8761/eureka/
 server:
   port: 8764
 spring:
   application:
     name: service-ribbon  

启动类

  • 在工程的启动类中, 通过 @EnableDiscoveryClient 向服务中心注册;

架构如下

image

创建EurekaServer

Idea初始化项目

  • 选择cloud discovery->eureka server 。

创建启动类

  • @EnableEurekaServer

配置参数

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

创建EurekaClient

初始化项目

  • 通server

创阿金启动类

  • @EnableEurekaClient

配置参数

 eureka:
   client:
     serviceUrl:
       defaultZone: http://localhost:8761/eureka/
 server:
   port: 8762
 spring:
   application:
     name: service-hi

  • wechatParent:
    • wechat-base-parent
    • wechat-business-parent
    • wechat-core-parent
    • wechat-web-parent

wechatParent

  • 职责: 一键构建所有需要发布的项目。
  • 特性
    • 所有项目初始时就带有这些 jar 包的依赖,例如:testng(单元测试相关),h2(单元测试相关),easymock(单元测试相关),lombok(根据注释自动生成 setter 和 getter)
    • 所有项目的额外特性,例如:单元测试插件
    • 项目发布管理,例如:私一的 maven 私服配置

wechat-core-parent

  • 职责:
    • 该部分与业务没有关联,只提供基础能力。例如:数据库持久能力,缓存能力,http封装能力,通用工具能力。
  • 通用特性:
    • javadoc插件,用于生成javadoc

wechat-base-parent

  • 只代表一个真实存在而且能独立存在的业务实体,简称base项目。

we-business-parent

  • 职责:
    • 它所聚合的的项目必须是一个提供 “共享” 业务流程,简称:business 项目。在这个流程过程中有可能需要引用 base 服务。它本身没有一个真实存在而且能独立存在的核心实体

wechat-web-parent

  • 职责:
    • 它所聚合的的项目可以通过互联网向用户提供服务,在产品规划上它自己独有的不被共享的业务,简称:web 项目。

总体架构图如下

image

Redis

  • 提到阿里云的这个 Redis,不得不吐槽一句,它竟然是不支持主从的,只能单实例,不过,用它做数据缓存,还真是蛮不错的选择,响应速度非常快。而且,因为是放置在内网的且只能内网访问,所以安全性也很高。

    MongoDB

  • 结构型数据,主要存储档案式的数据,比如每个用户的操作行为,以档案式记录并进行统计分析,方便下一阶段的项目做个性化服务。另外一些关联复杂的数据,也可以用 MongoDb 存储,可以提高访问速度。还有,一些对软件应用版本比较敏感的数据也可以存在 MongoDB 中,比如 a 版本拿到 A 数据,b 版本拿到 B 数据,而这个 AB 数据都是由很多关联关系复杂的数据所组成,如果把这些数据根据版本号存储在不同的 MongoDB 档案中,需要时,直接根据版本号拿就可以了,这样就避免了很多的 mysql 查询。

静态资源

  • OSS + CDN
    OSS 存储静态资源,CDN(内容分发网络) 可以加速静态资源的下载速度。至于资源链接地址,客户端可以通过接口访问从后端业务数据库中拿到。