食无求饱,居无求安,敏于事而慎于言.

0%

MySql 表索引设计原则

索引的优点

  • 1.加快数据的检索速度,这是创建索引的最主要的原因;

  • 2.通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性;

  • 3.加速表和表之间的连接;

  • 4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。

索引的缺点

  • 1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

  • 2.索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。

  • 3.当对表中的数据进行增加、删除和修改的时候,索引也要同步动态的维护,这样就降低了数据的增删改速度。

所以单表数据太少,索引反而会影响速度;更新非常频繁的数据不适宜建索引

索引设计原则

根据数据库的功能,可以在数据库设计器中创建三种索引

  • 唯一索引:唯一索引是不允许其中任何两行具有相同索引值的索引
  • 主键索引:表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时, 它允许对数据的快速访问
  • 聚集索引:表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表 只能包含一个聚集索引

选择索引的最终目的是为了使查询的速度变快。下面给出的原则是最基本的准则,但不能拘泥于这些准则,应该根据应用的实际情况进行分析和判断,选择最合适的索引方式。

1.索引最左匹配原则

  • 索引可以简单如一个列(a),也可以复杂如多个列(a, b, c, d),即联合索引。
  • 如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询(>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。因此,列的排列顺序决定了可命中索引的列数。

例子:

如有索引(a, b, c, d),查询条件a = 1 and b = 2 and c > 3 and d = 4,则会在每个节点依次命中a、b、c,无法命中d。(很简单:索引命中只能是相等的情况,不能是范围匹配)

明白最左匹配原则,对我们设计索引和编写高效SQL语句非常有帮助

2.为经常需要排序、分组操作的字段建立索引

经常需要ORDER BY、GROUP BY、DISTINCT等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。

分组字段或者排序字段应该创建索引

3.为常作为查询条件的字段建立索引

如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。

Where 子句中经常使用的字段应该创建索引

4.限制索引的数目

索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

5.尽量选择区分度高的列作为索引

尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

6.索引列不能参与计算

索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2019-12-02’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

即索引列不能带函数,否则会导致索引失效

7.扩展索引

尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

8.条件带like 注意事项

like 模糊查询中,右模糊查询(abc%)会使用索引,而(%abc)和(%abc%)会放弃索引而使用全表扫描

9.尽量使用数据量少的索引

如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间要比对CHAR(10)类型的字段需要的时间要多。

10.尽量使用前缀来索引

如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。

11.删除不再使用或者很少使用的索引

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

12.=和in可以乱序。

比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

13.联合查询

联合查询,子查询等多表操作时关联字段要加索引

资料

本项目演示如何使用 Spring Cloud Alibaba 完成 Dubbo 的RPC调用。

Spring Cloud与Dubbo

  • Spring Cloud是一套完整的微服务架构方案

  • Dubbo是国内目前非常流行的服务治理与RPC实现方案

由于Dubbo在国内有着非常大的用户群体,但是其周边设施与组件相对来说并不那么完善(比如feign,ribbon等等)。很多开发者使用Dubbo,又希望享受Spring Cloud的生态,因此也会有一些Spring Cloud与Dubbo一起使用的案例与方法出现。

Spring Cloud Alibaba的出现,实现了Spring Cloud与Dubbo的完美融合。在之前的教程中,我们已经介绍过使用Spring Cloud Alibaba中的Nacos来作为服务注册中心,并且在此之下可以如传统的Spring Cloud应用一样地使用Ribbon或Feign来实现服务消费。这篇,我们就来继续说说Spring Cloud Alibaba 下额外支持的RPC方案:Dubbo

代码实现

我们通过一个简单的例子,使用Nacos做服务注册中心,利用Dubbo来实现服务提供方与服务消费方。这里省略Nacos的安装与使用,下面就直接进入Dubbo的使用步骤。

定义 Dubbo 服务接口

创建 ali-nacos-dubbo-api 工程

Dubbo 服务接口是服务提供方与消费方的远程通讯契约,通常由普通的 Java 接口(interface)来声明,如 HelloService 接口:

1
2
3
public interface HelloService {
String hello(String name);
}

为了确保契约的一致性,推荐的做法是将 Dubbo 服务接口打包在jar包中,如以上接口就存放在 ali-nacos-dubbo-api 之中。 对于服务提供方而言,不仅通过依赖 artifact 的形式引入 Dubbo 服务接口,而且需要将其实现。对应的服务消费端,同样地需要依赖该 artifact, 并以接口调用的方式执行远程方法。接下来进一步讨论怎样实现 Dubbo 服务提供方和消费方。

实现 Dubbo 服务提供方

创建 ali-nacos-dubbo-provider,端口:9001 工程

pom.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?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">
<parent>
<artifactId>cloud-alibaba</artifactId>
<groupId>com.easy</groupId>
<version>1.0.0</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>ali-nacos-dubbo-provider</artifactId>

<dependencies>

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>

<!-- API -->
<dependency>
<groupId>com.easy</groupId>
<artifactId>ali-nacos-dubbo-api</artifactId>
<version>${project.version}</version>
</dependency>

<!--dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

bootstrap.yaml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
dubbo:
scan:
# dubbo 服务扫描基准包
base-packages: com.easy.andProvider.service

#Dubbo 服务暴露的协议配置,其中子属性 name 为协议名称,port 为协议端口( -1 表示自增端口,从 20880 开始)
protocol:
name: dubbo
port: -1

#Dubbo 服务注册中心配置,其中子属性 address 的值 “spring-cloud://localhost”,说明挂载到 Spring Cloud 注册中心
registry:
address: spring-cloud://localhost

spring:
application:
# Dubbo 应用名称
name: ali-nacos-dubbo-provider
main:
allow-bean-definition-overriding: true
cloud:
# Nacos 服务发现与注册配置
nacos:
discovery:
server-addr: 127.0.0.1:8848

Dubbo Spring Cloud 继承了 Dubbo Spring Boot 的外部化配置特性,也可以通过标注 @DubboComponentScan 来实现基准包扫描。

实现 Dubbo 服务

HelloService 作为暴露的 Dubbo 服务接口,服务提供方 ali-nacos-dubbo-provider 需要将其实现:

1
2
3
4
5
6
7
8
9
10
11
12
package com.easy.andProvider.service;

import com.easy.and.api.service.HelloService;
import org.apache.dubbo.config.annotation.Service;

@Service
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "你好 " + name;
}
}

import org.apache.dubbo.config.annotation.Service 是 Dubbo 服务注解,仅声明该 Java 服务实现为 Dubbo 服务

贴上启动类代码:

1
2
3
4
5
6
7
@EnableDiscoveryClient
@EnableAutoConfiguration
public class AndProviderApplication {
public static void main(String[] args) {
SpringApplication.run(AndProviderApplication.class);
}
}

实现 Dubbo 服务消费方

创建 ali-nacos-dubbo-consumer,端口:9103 工程

pom.xml依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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">
<parent>
<artifactId>cloud-alibaba</artifactId>
<groupId>com.easy</groupId>
<version>1.0.0</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>ali-nacos-dubbo-consumer</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>

<dependency>
<groupId>com.easy</groupId>
<artifactId>ali-nacos-dubbo-api</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

yaml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dubbo:
registry:
address: spring-cloud://localhost
cloud:
subscribed-services: ali-nacos-dubbo-provider

spring:
application:
name: ali-nacos-dubbo-consumer
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

实现 Dubbo 服务消费方

HomeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.easy.andConsumer;

import com.easy.and.api.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class HomeController {

@Reference
HelloService helloService;

@GetMapping("/hello")
public String hello(String name) {
return helloService.hello("云天");
}
}

AndConsumerApplication.java启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.easy.andConsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class AndConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(AndConsumerApplication.class, args);
}
}

使用示例

示例关联项目

本示例我们创建了三个项目实现

  • ali-nacos-dubbo-api:定义Dubbo服务接口工程

  • ali-nacos-dubbo-provider:Dubbo服务提供方并向nacos注册服务,服务名:ali-nacos-dubbo-provider,端口:9001

  • ali-nacos-dubbo-consumer:Dubbo服务消费方并向nacos注册服务,服务名:ali-nacos-dubbo-consumer,端口:9103

运行示例测试

首先要启动服务注册中心 nacos、ali-nacos-dubbo-provider服务及ali-nacos-dubbo-consumer服务

返回

1
你好 云天

或者你也可以通过 curl 命令执行 HTTP GET 方法

1
$curl http://127.0.0.1:9103/hello

HTTP 响应为:

1
你好 云天

以上结果说明应用 ali-nacos-dubbo-consumer 通过消费 Dubbo 服务,返回服务提供方 ali-nacos-dubbo-provider 运算后的内容。

以上我们完成了 Dubbo 服务提供方和消费方的入门运用,源代码请直接参考模块:

资料

本项目演示如何使用 Sentinel 完成 Spring Cloud 应用的熔断降级调用。

Sentinel 是阿里巴巴开源的分布式系统的流量防卫组件,Sentinel 把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。

OpenFeign是一款声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API,需要了解OpenFeign使用基础,可以参考cloud-feign示例源码

本项目服务注册中心使用nacos,服务提供者使用Spring Cloud Alibaba(一) 如何使用nacos服务注册和发现创建的ali-nacos-provider服务

Sentinel介绍

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 1.丰富的应用场景

Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 2.完备的实时监控

Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 3.广泛的开源生态

Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 4.完善的 SPI 扩展点

Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 分为两个部分

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

Sentinel主要特性

Sentinel 主要特性

Sentinel开源生态

Sentinel 开源生态

熔断降级

对调用链路中不稳定的资源进行熔断降级是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)

降级策略

  • 平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。
  • 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态。
  • 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。

熔断降级代码实现

服务提供方

创建ali-nacos-provider项目

  1. 首先, 依赖nacos 注册中心
1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 定义服务提供方接口
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@Slf4j
public class HelloController {
@GetMapping(value = "/hello/{str}", produces = "application/json")
public String hello(@PathVariable String str) {
log.info("-----------收到消费者请求-----------");
log.info("收到消费者传递的参数:" + str);
String result = "我是服务提供者,见到你很高兴==>" + str;
log.info("提供者返回结果:" + result);
return result;
}
}

服务提消费方

创建ali-nacos-sentinel-feign项目

1.首先,pom.xml添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

</dependencies>

2.定义FeignClient,及其降级配置

  • 定义FeignClient
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.easy.ansFeign.service;

import com.easy.ansFeign.fallback.HelloServiceFallbackFactory;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "ali-nacos-provider", fallbackFactory = HelloServiceFallbackFactory.class)
public interface HelloService {

/**
* 调用服务提供方的输出接口.
*
* @param str 用户输入
* @return hello result
*/
@GetMapping("/hello/{str}")
String hello(@PathVariable("str") String str);
}
  • 定义fallback 工厂,获取异常
1
2
3
4
5
6
7
8
@Component
public class HelloServiceFallbackFactory implements FallbackFactory<HelloServiceFallback> {

@Override
public HelloServiceFallback create(Throwable throwable) {
return new HelloServiceFallback(throwable);
}
}
  • 定义具体的fallback 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HelloServiceFallback implements HelloService {

private Throwable throwable;

HelloServiceFallback(Throwable throwable) {
this.throwable = throwable;
}

/**
* 调用服务提供方的输出接口.
*
* @param str 用户输入
* @return
*/
@Override
public String hello(String str) {
return "服务调用失败,降级处理。异常信息:" + throwable.getMessage();
}
}
  • 测试入口
1
2
3
4
5
6
7
8
9
10
11
@RestController
public class TestController {

@Autowired
private HelloService helloService;

@GetMapping("/hello-feign/{str}")
public String feign(@PathVariable String str) {
return helloService.hello(str);
}
}

使用示例

示例关联项目

Spring Cloud Alibaba(一) 如何使用nacos服务注册和发现基础上,我们新建了ali-nacos-sentinel-feign项目,并调用ali-nacos-provider项目用作该示例的服务提供方,有以下二个项目做测试。

  • ali-nacos-provider:服务提供者,服务名:ali-nacos-provider,端口:9000

  • ali-nacos-sentinel-feign:服务消费者,服务名:ali-nacos-sentinel-feign,端口:9102

运行示例测试

首先要启动服务注册中心 nacos、ali-nacos-provider服务及ali-nacos-sentinel-feign服务

返回

1
我是服务提供者,见到你很高兴==>yuntian

表示我们的服务成功调用到了

返回

1
服务调用失败,降级处理。异常信息:com.netflix.client.ClientException: Load balancer does not have available server for client: ali-nacos-provider

表示执行了我们预定的回调,服务成功降级了。

资料

介绍

之前Spring Cloud Config基础篇这篇文章介绍了Spring Cloud Config 配置中心基础的实现,今天继续聊下Spring Cloud Config 并结合nacos做服务注册中心,实现多项目、多配置文件、按项目目录划分等功能的配置服务中心。

阅读本篇文章之前,最好要有nacos基础;关于nacos是什么,如何使用,可以参考我的上一篇文章 Spring Cloud Alibaba(一) 如何使用nacos服务注册和发现,或者直接链接到官网教程Nacos 快速开始

本示例主要内容

  • 采用nacos做服务注册中心,Spring Cloud Config做配置服务中心,在上一篇基础上新建了ali-nacos-config-server配置服务中心项目、ali-nacos-config-client配置客户端项目、并把ali-nacos-consumer-feign配置也调整成从配置中心加载配置
  • 支持多项目,config-repo配置文件目录按项目名称来规划,在配置中心 searchPaths: /cloud-alibaba/config-repo/{application}/ 使用application自动识别查找目录
  • 支持单项目多配置文件,ali-nacos-config-client项目的配置文件 spring.cloud.config.name=${spring.application.name},myconfig,通过指定多个name实现多配置文件

实现示例过程

新建ali-nacos-config-server项目

该项目用来做配置服务中心,以下贴出关键部分代码

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

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

</dependencies>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8001

spring:
application:
name: ali-nacos-config-server
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server:
git:
#uri: https://github.com/smltq/spring-boot-demo.git
uri: https://gitee.com/tqlin/spring-boot-demo.git
searchPaths: /cloud-alibaba/config-repo/{application}/
force-pull: true

启动类AnConfigServerApplication.java

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class AnConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(AnConfigServerApplication.class, args);
}
}

新建ali-nacos-config-client项目

该项目用来做配置中心客户端测试之一,以下贴出几处关键代码

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

</dependencies>

bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
application:
name: ali-nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
name: ${spring.application.name},myconfig
uri: http://localhost:8001/ # config server 配置服务地址
profile: ${spring.profiles.active}
label: master
profiles:
active: pro # 配置文件版本(该示例分为test,dev,pro)

写个配置读取测试类HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class HelloController {
@Value("${easy.hello}")
private String hello;

@Value("${easy.myconfig}")
private String myconfig;

@RequestMapping("/hello")
public Map hello() {
Map map = new HashMap<>();
map.put("hello", hello);
map.put("myconfig", myconfig);
return map;
}
}

启动类AnConfigClientApplication.java

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class AnConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(AnConfigClientApplication.class, args);
}
}

调整ali-nacos-consumer-feign项目

以下贴出调整部分代码

pom.xml增加spring-cloud-starter-config依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

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

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>

yml配置文件增加bootstrap.yml,把核心配置移到该配置文件
bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
application:
name: ali-nacos-consumer-feign
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
name: ${spring.application.name}
uri: http://localhost:8001/ # config server 配置服务地址
profile: ${spring.profiles.active}
label: master
profiles:
active: dev # 配置文件版本(该示例分为test,dev,pro)

编写配置读写测试类HomeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RestController
@Slf4j
public class HomeController {

@Autowired
private HelloService helloService;

@Value("${easy.hello}")
private String hello;

@GetMapping(value = "/", produces = "application/json")
public String home() {
log.info("-----------------consumer调用开始-----------------");
String param = "云天";
log.info("消费者传递参数:" + param);
String result = helloService.hello(param);
log.info("收到提供者响应:" + result);
return "feign消费者" + result;
}

@RequestMapping("/hello")
public Map hello() {
Map map = new HashMap<>();
map.put("hello", hello);
return map;
}
}

最后放上配置文件目录规划

使用示例

在上一篇基础上,我们新建了2个项目,并调整ali-nacos-consumer-feign项目使它支持配置远程读取,有以下三个项目做测试。

ali-nacos-config-server:配置服务中心,服务名:ali-nacos-config-server,端口:8001
ali-nacos-config-client:配置客户端1(消费端),服务名:ali-nacos-config-client,端口:8002
ali-nacos-consumer-feign:配置客户端2(消费端),服务名:ali-nacos-consumer-feign,端口:9101

运行测试

首先要启动服务注册中心 nacos

启动ali-nacos-config-server服务,配置服务中心测试

1
2
3
4
5
6
7
8
9
10
{
name: "ali-nacos-config-client",
profiles: [
"dev"
],
label: null,
version: "5456d7ca31d46e91464b6efd3a0831a8208413d9",
state: null,
propertySources: [ ]
}
  • 访问:http://localhost:8001/ali-nacos-config-client/test ,返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    name: "ali-nacos-config-client",
    profiles: [
    "test"
    ],
    label: null,
    version: "5456d7ca31d46e91464b6efd3a0831a8208413d9",
    state: null,
    propertySources: [ ]
    }
    这表示配置能正确从git上加载到了。

启动ali-nacos-config-client服务,运行客户端测试1

1
2
3
4
{
hello: "ali-nacos-config-client 项目的 dev config",
myconfig: "ali-nacos-config-client 项目的 myconfig config"
}
1
2
3
4
{
hello: "ali-nacos-config-client 项目的 test config",
myconfig: "ali-nacos-config-client 项目的 myconfig config"
}

表示我git上该项目的2个配置文件都成功读取到了。

启动ali-nacos-consumer-feign项目,测试客户端测试2

访问:http://localhost:9101/hello

返回结果

1
2
3
{
hello: "ali-nacos-consumer-feign 项目的 dev config"
}

表示该项目的配置文件加载成功了

资料

Nacos介绍

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

Nacos 的关键特性

  • 服务发现和服务健康监测
  • 动态配置服务,带管理界面,支持丰富的配置维度。
  • 动态 DNS 服务
  • 服务及其元数据管理

Nacos下载及部署

官方介绍文档:Nacos 快速开始或者直接下载zip包,部署下载

windows环境部署过程遇到的问题汇总

jdk版本要求

一定要注意,jdk版本要求 64bit JDK 1.8+

运行报错

运行startup.cmd,一闪而过。打开startup.cmd脚本,在最后一行添加 pause 使报错不会立即结束方便查看报错信息,这时会发现以下报错信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/springfra
mework/boot/loader/PropertiesLauncher : Unsupported major.minor version 52.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:14
2)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

产生以上问题的原因是我电脑上有些老项目使用jdk 1.7,所以项目是jdk1.7和jdk1.8交叉着使用。在cmd里查看版本 java -version 输出然后是1.8.0_211(只会输出最高的版本)

解决方案

  • 配置java_home,增加jdk1.8环境变量,怎么添加环境变量需要自行百度,以下贴出我的java环境变量
1
2
3
4
5
6
7
C:\Users\Administrator>set java_home
JAVA_HOME=D:\Program Files\Java\jdk1.7.0_71

C:\Users\Administrator>set java8_home
JAVA8_HOME=D:\Program Files\Java\jdk1.8.0_201

C:\Users\Administrator>
  • 调整startup.cmd脚本,使用java8_home变量,使它能正确调用jdk1.8而非jdk1.7,以下是调整部分代码
1
2
if not exist "%JAVA8_HOME%\bin\java.exe" echo Please set the JAVA8_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1
set "JAVA=%JAVA8_HOME%\bin\java.exe"

这里只做个替换,把原来的JAVA_HOME规划成JAVA8_HOME,运行startup.cmd,此时能正确运行Nacos服务了

查看界面

启动成功,在浏览器上访问:http://localhost:8848/nacos ,会跳转到登陆界面,默认的登陆用户名为nacos,密码也为nacos。

登陆成功后,就可以操作管理界面了

使用Nacos服务注册和发现

要使用nacos,需要在pom.xml添加必要的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

服务注册与发现示例代码创建

在本案例中,使用3个服务注册到Nacos上,分别为服务提供者ali-nacos-provider和负载均衡ribbon消费者ali-nacos-consumer-ribbon、申明式服务调用feign消费者ali-nacos-consumer-feign。

什么是ribbon和feign,及使用示例这里不重复介绍,需要了解可以查看示例

创建服务提供者ali-nacos-provider

pom.xml添加nacos依赖

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

application.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 9000 #指定为9000端口

spring:
application:
name: ali-nacos-provider #服务名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #服务注册地址(nacos默认为8848端口)

management:
endpoints:
web:
exposure:
include: '*'

启动类增加 @EnableDiscoveryClient 注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.easy.aliNacosProvider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class AliNacosProviderApplication {

public static void main(String[] args) {
SpringApplication.run(AliNacosProviderApplication.class, args);
}
}

写个hello服务接口 HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.easy.aliNacosProvider;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class HelloController {
@GetMapping(value = "/hello/{str}", produces = "application/json")
public String hello(@PathVariable String str) {
log.info("-----------收到消费者请求-----------");
log.info("收到消费者传递的参数:" + str);
String result = "我是服务提供者,见到你很高兴==>" + str;
log.info("提供者返回结果:" + result);
return result;
}
}

创建ribbon服务消费者

pom.xml增加nocos和ribbon依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>

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

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

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

</dependencies>

application.yml

1
2
3
4
5
6
7
8
9
10
server:
port: 9100

spring:
application:
name: ali-nacos-consumer-ribbon
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

服务调用HomeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.easy.ancRibbon;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Slf4j
public class HomeController {
@Autowired
private RestTemplate restTemplate;

@GetMapping(value = "/", produces = "application/json")
public String home() {
log.info("-----------------consumer调用开始-----------------");
String param = "云天";
log.info("消费者传递参数:" + param);
String result = restTemplate.getForObject("http://ali-nacos-provider/hello/" + param, String.class);
log.info("收到提供者响应:" + result);
return "ribbon消费者," + result;
}
}

启用类AncRibbonConsumerApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.easy.ancRibbon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class AncRibbonConsumerApplication {

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(AncRibbonConsumerApplication.class, args);
}

}

创建feign服务消费者

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

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

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 9101

spring:
application:
name: ali-nacos-consumer-feign
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
main:
allow-bean-definition-overriding: true #允许一样的beanName

这里有个坑要注意下,如果allow-bean-definition-overriding没有设置为true,运行会报如下错:

1
错误: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

这里有问题的详细介绍问题原因

申请服务HelloService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.easy.ancFeign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient("ali-nacos-provider")
public interface HelloService {

@RequestMapping(path = "hello/{str}")
String hello(@RequestParam("str") String param);

}

服务调用HomeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.easy.ancFeign;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class HomeController {

@Autowired
private HelloService helloService;

@GetMapping(value = "/", produces = "application/json")
public String home() {
log.info("-----------------consumer调用开始-----------------");
String param = "云天";
log.info("消费者传递参数:" + param);
String result = helloService.hello(param);
log.info("收到提供者响应:" + result);
return "feign消费者" + result;
}
}

启动类AncFeignConsumerApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.easy.ancFeign;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients(basePackages = {"com.easy.ancFeign"})
public class AncFeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(AncFeignConsumerApplication.class, args);
}
}

使用示例

现有三个项目如下

ali-nacos-provider:服务提供者1,服务名:ali-nacos-provider,端口:9000
ali-nacos-consumer-ribbon:ribbon服务消费者,服务名:ali-nacos-consumer-ribbon,端口:9100
ali-nacos-consumer-feign:feign消费者,服务名:ali-nacos-consumer-feign,端口:9101

运行测试

首先要启动服务注册中心 nacos
其次,分别启动ali-nacos-provider、ali-nacos-consumer-ribbon、ali-nacos-consumer-feign三个服务

  • 访问地址:http://localhost:9100/ ,返回:ribbon消费者,我是服务提供者,见到你很高兴==>云天,说明ribbon消费成功了。
  • 访问地址:http://localhost:9101/ ,返回:feign消费者我是服务提供者,见到你很高兴==>云天,说明feign消费费成功了。
  • 访问地址:http://localhost:9000/hello/yuntian ,返回:我是服务提供者,见到你很高兴==>yuntian,说明服务提供者访问成功了(PS:服务提供者一般是不对外公开的,怎么隐蔽接口将在接下去的文章里介绍)
  • 访问地址:http://localhost:8848/nacos/#/login ,输入用户:nacos,密码:nacos。进入服务管理界面,在服务管理-服务列表里可以看到我们运行的三个服务了。

资料

问题收集

Java

mysql

redis

常用命令

Docker

其它

LeetCode 按序打印

第1114题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

我们提供了一个类:

public class Foo {
  public void one() { print("one"); }
  public void two() { print("two"); }
  public void three() { print("three"); }
}
三个不同的线程将会共用一个 Foo 实例。

线程 A 将会调用 one() 方法
线程 B 将会调用 two() 方法
线程 C 将会调用 three() 方法
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。

 

示例 1:

输入: [1,2,3]
输出: "onetwothree"
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。
正确的输出是 "onetwothree"
示例 2:

输入: [1,3,2]
输出: "onetwothree"
解释:
输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。
正确的输出是 "onetwothree"
 

注意:

尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。

你看到的输入格式主要是为了确保测试的全面性。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-in-order

解题思路

  • 1.定义一个flag信号量及对象锁lock
  • 2.定义三个方法first,second,third用来分别执行A,B,C三个线程,并且在run()前增加限制,执行后更新flag值。比如:first执行条件为flag=0(即C线程执行完),A线程执行完后,flag设置为1表示可以执行B线程了
  • 3.通过以上设置,保存了A,B,C线程按顺序执行,这里题目要求从A线程要第一个执行,所以要把flag信号量初始值为0

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
public class Sub1114 {
public static void main(String[] args) throws InterruptedException {
//测试用例字符串
int[] runOrder = new int[]{2, 3, 1};

//生成结果字符串
StringBuffer result = new StringBuffer();

Runnable one = () -> result.append("one");
Runnable two = () -> result.append("two");
Runnable three = () -> result.append("three");

Foo foo = new Foo();

Thread threads[] = new Thread[runOrder.length];
for (int i = 0; i < runOrder.length; ++i) {
Thread thread = null;
if (runOrder[i] == 1) {
thread = new FirstThread(foo, one);
} else if (runOrder[i] == 2) {
thread = new SecondThread(foo, two);
} else if (runOrder[i] == 3) {
thread = new ThirdThread(foo, three);
}
thread.start();
threads[i] = thread;
}

//等侍所有线程执行完
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}

//输出结果串
System.out.println(result.toString());
}
}

class FirstThread extends Thread {
Foo foo;
Runnable runnable;

public FirstThread(Foo h2o, Runnable runnable) {
this.foo = h2o;
this.runnable = runnable;
}

@Override
public void run() {
try {
foo.first(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class SecondThread extends Thread {
Foo foo;
Runnable runnable;

public SecondThread(Foo h2o, Runnable runnable) {
this.foo = h2o;
this.runnable = runnable;
}

@Override
public void run() {
try {
foo.second(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class ThirdThread extends Thread {
Foo foo;
Runnable runnable;

public ThirdThread(Foo h2o, Runnable runnable) {
this.foo = h2o;
this.runnable = runnable;
}

@Override
public void run() {
try {
foo.third(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class Foo {
//信号量
private int flag = 0;
//定义Object对象为锁
private Object lock = new Object();

public Foo() {
}

public void first(Runnable printFirst) throws InterruptedException {
synchronized (lock) {
//如果flag不为0则让first线程等待,while循环控制first线程如果不满住条件就一直在while代码块中,防止出现中途跳入,执行下面的代码,其余线程while循环同理
while (flag != 0) {
lock.wait();
}

printFirst.run();
//定义成员变量为 1
flag = 1;
//唤醒其余所有的线程
lock.notifyAll();
}
}

public void second(Runnable printSecond) throws InterruptedException {
synchronized (lock) {
//如果成员变量不为1则让二号等待
while (flag != 1) {
lock.wait();
}

printSecond.run();
//如果成员变量为 1 ,则代表first线程刚执行完,所以执行second,并且改变成员变量为 2
flag = 2;
//唤醒其余所有的线程
lock.notifyAll();
}
}

public void third(Runnable printThird) throws InterruptedException {
synchronized (lock) {
//如果flag不等于2 则一直处于等待的状态
while (flag != 2) {
lock.wait();
}

//如果成员变量为 2 ,则代表second线程刚执行完,所以执行third,并且改变成员变量为 0
printThird.run();
flag = 0;
lock.notifyAll();
}
}
}

资料

LeetCode 交替打印FooBar

第1115题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

我们提供一个类:

class FooBar {
public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
  }
}

public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
}
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。

请设计修改程序,以确保 "foobar" 被输出 n 次。

 

示例 1:

输入: n = 1
输出: "foobar"
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
示例 2:

输入: n = 2
输出: "foobarfoobar"
解释: "foobar" 将被输出两次。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-foobar-alternately

解题思路

  • 1.定义一个flag信号量及对象锁lock
  • 2.定义信号量flag及锁对象lock,分别给foo和bar方法的循环内加锁
  • 3.当flag为0时,执行printFoo.run();flag为1时执行printBar.run();调用run后,分别更新flag值,使它能够交替等待
  • 4.通过以上设置,A、B线程能够交替执行,否则循环等待了。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public class Sub1115 {
public static void main(String[] args) throws InterruptedException {
//测试用例字符串
int n = 2;

//生成结果字符串
StringBuffer result = new StringBuffer();

Runnable foo = () -> result.append("foo");
Runnable bar = () -> result.append("bar");

FooBar fooBar = new FooBar(n);

Thread threads[] = new Thread[n];
Thread fooThread = new FooThread(fooBar, foo);
Thread barThread = new BarThread(fooBar, bar);

fooThread.start();
barThread.start();

threads[0] = fooThread;
threads[1] = barThread;

//等侍所有线程执行完
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}

//输出结果串
System.out.println(result.toString());
}
}

class FooThread extends Thread {
FooBar fooBar;
Runnable runnable;

public FooThread(FooBar fooBar, Runnable runnable) {
this.fooBar = fooBar;
this.runnable = runnable;
}

@Override
public void run() {
try {
fooBar.foo(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class BarThread extends Thread {
FooBar fooBar;
Runnable runnable;

public BarThread(FooBar fooBar, Runnable runnable) {
this.fooBar = fooBar;
this.runnable = runnable;
}

@Override
public void run() {
try {
fooBar.bar(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class FooBar {
private int n;

//信号量
private int flag = 0;

//定义Object对象为锁
private Object lock = new Object();

public FooBar(int n) {
this.n = n;
}

public void foo(Runnable printFoo) throws InterruptedException {

for (int i = 0; i < n; i++) {
synchronized (lock) {
while (flag != 0) {
lock.wait();
}
printFoo.run();
flag = 1;
lock.notifyAll();
}
}
}

public void bar(Runnable printBar) throws InterruptedException {

for (int i = 0; i < n; i++) {
synchronized (lock) {
while (flag != 1) {
lock.wait();
}

printBar.run();
flag = 0;
lock.notifyAll();
}
}
}
}

资料

LeetCode H2O 生成

第1117题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
现在有两种线程,氢 oxygen 和氧 hydrogen,你的目标是组织这两种线程来产生水分子。

存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来。

氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。

这些线程应该三三成组突破屏障并能立即组合产生一个水分子。

你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。

换句话说:

如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。
如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。
书写满足这些限制条件的氢、氧线程同步代码。

 

示例 1:

输入: "HOH"
输出: "HHO"
解释: "HOH""OHH" 依然都是有效解。
示例 2:

输入: "OOHHHH"
输出: "HHOHHO"
解释: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH""OHHOHH" 依然都是有效解。
 

限制条件:

输入字符串的总长将会是 3n, 1 ≤ n ≤ 50
输入字符串中的 “H” 总数将会是 2n;
输入字符串中的 “O” 总数将会是 n。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/building-h2o

解题思路

  • 1.设定一个h信息号
  • 2.每生成一个h,当h等于2的时候则进入等待o。否则生成氢气并且h自加1。
  • 3.每生成一个o,当h小于2的时候则进入等侍h。否则生成o并且h清0,重新开始生成一个水份子
  • 4.注意Runnable和Thread的区别,实现了Runnable接口,无法启动线程,必须依托其他类或线程

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class Sub1117 {
public static void main(String[] args) throws InterruptedException {
//测试用例字符串
String test = "HOHOHHOOHOHHHHHOHHHOH";

//生成结果字符串
StringBuffer result = new StringBuffer();

//注意:创建的Runnable任务,无法启动线程,必须依托其他类或线程启动
//创建生成氧气任务
Runnable releaseHydrogen = () -> result.append("H");

//创建生成氧气任务
Runnable releaseOxygen = () -> result.append("O");

//保存线程数组
Thread threads[] = new Thread[test.length()];

H2O h2o = new H2O();
for (int i = 0; i < test.length(); ++i) {
Thread thread = null;
//根据获得的字符调用相应的氧气或氢气线程
if (test.charAt(i) == 'O') {
thread = new OGenerator(h2o, releaseOxygen);
} else if (test.charAt(i) == 'H') {
thread = new HGenerator(h2o, releaseHydrogen);
}
//开始线程
thread.start();
//保存到线程数组
threads[i] = thread;
}

//等侍所有线程执行完
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}

//输出结果串
System.out.println(result.toString());
}
}

//氢气生成线程
class HGenerator extends Thread {
H2O h2o;
Runnable releaseHydrogen;

public HGenerator(H2O h2o, Runnable releaseHydrogen) {
this.h2o = h2o;
this.releaseHydrogen = releaseHydrogen;
}

@Override
public void run() {
try {
h2o.hydrogen(releaseHydrogen);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

//氧气生成线程
class OGenerator extends Thread {
H2O h2o;
Runnable releaseOxygen;

public OGenerator(H2O h2o, Runnable releaseOxygen) {
this.h2o = h2o;
this.releaseOxygen = releaseOxygen;
}

@Override
public void run() {
try {
h2o.oxygen(releaseOxygen);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class H2O {
public H2O() {
}

int h = 0;

public synchronized void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
while (h == 2) {
this.wait();
}
releaseHydrogen.run();
++h;
this.notify();
}

public synchronized void oxygen(Runnable releaseOxygen) throws InterruptedException {
while (h < 2) {
this.wait();
}
releaseOxygen.run();
h = 0;
this.notify();
}
}

资料

Centos7部署mysql

安装mysql

yum install mysql mysql-server

会出现以下错误:

1
2
3
4
5
6
7
8
[root@yl-web yl]# yum install mysql-server
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.sina.cn
* extras: mirrors.sina.cn
* updates: mirrors.sina.cn
No package mysql-server available.
Error: Nothing to do

出现这个问题的原因是,CentOS 7 版本将MySQL数据库软件从默认的程序列表中移除,用mariadb代替。

解决办法1:安装mariadb

MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可。开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL闭源的潜在风险,因此社区采用分支的方式来避开这个风险。MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。

安装mariadb,文件几十M左右大小

yum install mariadb-server mariadb

mariadb数据库的相关命令是:

  • systemctl start mariadb #启动MariaDB
  • systemctl stop mariadb #停止MariaDB
  • systemctl restart mariadb #重启MariaDB
  • systemctl enable mariadb #设置开机启动

解决办法2:官网下载安装mysql-server

安装前,我们可以检测系统是否自带安装 MySQL:

rpm -qa | grep mysql

如果系统有安装,那可以选择进行卸载:

1
2
rpm -e mysql  // 普通删除模式
rpm -e --nodeps mysql // 强力删除模式,如果使用上面命令删除时,提示有依赖的其它文件,则用该命令可以对其进行强力删除

1.安装 MySQL

接下来我们在 Centos7 系统下使用 yum 命令安装 MySQL,需要注意的是 CentOS 7 版本中 MySQL数据库已从默认的程序列表中移除,所以在安装前我们需要先去官网下载 Yum 资源包,下载地址为:https://dev.mysql.com/downloads/repo/yum/

1
2
3
4
wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm
yum update
yum install mysql-server

2.初始化 MySQL

1
mysqld --initialize

3.启动 MySQL

1
systemctl start mysqld

4.查看 MySQL 运行状态

1
systemctl status mysqld

5.验证 MySQL 安装

1
mysqladmin --version

输出类似以下信息,表示安装成功了

mysqladmin Ver 8.42 Distrib 5.6.46, for Linux on x86_64

如果以上命令执行后未输出任何信息,说明你的Mysql未安装成功。

6.密码设置

1
mysqladmin -u root password "new_password";

1.首先配置允许访问的用户,采用授权的方式给用户权限

1
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY '123456' WITH GRANT OPTION

root是登陆数据库的用户,123456是登陆数据库的密码

2.配置好权限之后,刷新生效

flush privileges

资料