文章目录
代码git地址
一.springcloud简介
springcloud:多种微服务架构落地技术的集合体;
京东技术架构:
阿里技术架构
springcloud涵盖的技术:
核心技术:
二.版本选择
1.查询版本
springcloud版本
{
"git": {
"commit": {
"time": "2020-03-19T13:43:47Z",
"id": "6a8d5ce"
},
"branch": "6a8d5ce9a63e30db3fa1c23c64a6b7cef2072c71"
},
"build": {
"version": "0.0.1-SNAPSHOT",
"artifact": "start-site",
"name": "start.spring.io website",
"versions": {
"initializr": "0.9.0.BUILD-SNAPSHOT",
"spring-boot": "2.2.5.RELEASE"
},
"group": "io.spring.start",
"time": "2020-03-19T13:45:29.062Z"
},
"bom-ranges": {
"azure": {
"2.0.10": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE",
"2.1.7": "Spring Boot >=2.1.0.RELEASE and <2.2.0.M1",
"2.2.0": "Spring Boot >=2.2.0.M1"
},
"codecentric-spring-boot-admin": {
"2.0.6": "Spring Boot >=2.0.0.M1 and <2.1.0.M1",
"2.1.6": "Spring Boot >=2.1.0.M1 and <2.2.0.M1",
"2.2.1": "Spring Boot >=2.2.0.M1"
},
"spring-cloud": {
"Finchley.M2": "Spring Boot >=2.0.0.M3 and <2.0.0.M5",
"Finchley.M3": "Spring Boot >=2.0.0.M5 and <=2.0.0.M5",
"Finchley.M4": "Spring Boot >=2.0.0.M6 and <=2.0.0.M6",
"Finchley.M5": "Spring Boot >=2.0.0.M7 and <=2.0.0.M7",
"Finchley.M6": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1",
"Finchley.M7": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2",
"Finchley.M9": "Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE",
"Finchley.RC1": "Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE",
"Finchley.RC2": "Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE",
"Finchley.SR4": "Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT",
"Finchley.BUILD-SNAPSHOT": "Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3",
"Greenwich.M1": "Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE",
"Greenwich.SR5": "Spring Boot >=2.1.0.RELEASE and <2.1.14.BUILD-SNAPSHOT",
"Greenwich.BUILD-SNAPSHOT": "Spring Boot >=2.1.14.BUILD-SNAPSHOT and <2.2.0.M4",
"Hoxton.SR3": "Spring Boot >=2.2.0.M4 and <2.3.0.BUILD-SNAPSHOT",
"Hoxton.BUILD-SNAPSHOT": "Spring Boot >=2.3.0.BUILD-SNAPSHOT"
},
"spring-cloud-alibaba": {
"2.2.0.RELEASE": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1"
},
"spring-cloud-services": {
"2.0.3.RELEASE": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE",
"2.1.7.RELEASE": "Spring Boot >=2.1.0.RELEASE and <2.2.0.RELEASE",
"2.2.3.RELEASE": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1"
},
"spring-statemachine": {
"2.0.0.M4": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1",
"2.0.0.M5": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2",
"2.0.1.RELEASE": "Spring Boot >=2.0.0.RELEASE"
},
"vaadin": {
"10.0.17": "Spring Boot >=2.0.0.M1 and <2.1.0.M1",
"14.1.20": "Spring Boot >=2.1.0.M1"
}
},
"dependency-ranges": {
"okta": {
"1.2.1": "Spring Boot >=2.1.2.RELEASE and <2.2.0.M1",
"1.4.0": "Spring Boot >=2.2.0.M1"
},
"mybatis": {
"2.0.1": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE",
"2.1.2": "Spring Boot >=2.1.0.RELEASE"
},
"geode": {
"1.2.5.RELEASE": "Spring Boot >=2.2.0.M5 and <2.3.0.M1",
"1.3.0.M2": "Spring Boot >=2.3.0.M1 and <2.3.0.BUILD-SNAPSHOT",
"1.3.0.BUILD-SNAPSHOT": "Spring Boot >=2.3.0.BUILD-SNAPSHOT"
},
"camel": {
"2.22.4": "Spring Boot >=2.0.0.M1 and <2.1.0.M1",
"2.24.3": "Spring Boot >=2.1.0.M1 and <2.2.0.M1",
"3.1.0": "Spring Boot >=2.2.0.M1"
},
"solace": {
"3.1.0": "Spring Boot >=2.1.0.RELEASE and <2.2.0.M1",
"3.2.0": "Spring Boot >=2.2.0.M1"
}
}
}
2.博文选用版本
三.cloud的停更、升级、替换
参考资料
四.微服务架构编码构建
1.约定》配置》编码
2.新建工程
注解生效激活
编译版本选择
3.父工程pom
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud2020</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>cloud-provider-payment8001</module>
</modules>
<packaging>pom</packaging>
<!-- 统一管理jar包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version -->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<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>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.maven中dependencyManagement
通常会在最顶层父项目pom文件中看到 dependencyManagement元素,让所有的子项目引入一个依赖而不用显式的列出版本号。maven会沿着父子项目向上走,找到拥有dependencyManagement元素的项目,使用其中的版本号;
优点在于:实现一处修改,处处修改;且子项目中若引入不同的版本,仅需添加对应的版本号即可;
5.maven跳过单元测试
6.rest微服务项目构建
a.建项目
查看父工程pom文件
b.改pom
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.cz.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
c.写yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://IP地址:端口号/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.cz.springcloud.entities
d.启动类
e.业务类
建表
CREATE TABLE `payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`serial` varchar(200) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@Data
/*全参*/
@AllArgsConstructor
/*空参*/
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
private T data;
/*数据为空*/
public CommonResult(Integer code, String message) {
this.code = code;
this.message = message;
}
}
/*推荐使用Mapper,少用Repository*/
@Mapper
public interface PaymentDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cz.springcloud.dao.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment (serial) values (#{serial});
</insert>
<resultMap id="BaseResultMap" type="Payment">
<id column="id" property="id" jdbcType="BIGINT"></id>
<id column="serial" property="serial" jdbcType="VARCHAR"></id>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
</mapper>
public interface PaymentService {
int create(Payment payment);
Payment getPaymentById( Long id);
}
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@PostMapping(value = "/payment/create")
public CommonResult<Payment> create(Payment payment) {
int result = paymentService.create(payment);
log.info("插入结果: "+ result);
if(result>0){
return new CommonResult(200,"插入数据成功",result);
}else {
return new CommonResult(444,"插入数据失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id")Long id) {
Payment payment = paymentService.getPaymentById(id);
log.info("查询结果: "+ payment);
if(payment!=null){
return new CommonResult(200,"查询数据成功",payment);
}else {
return new CommonResult(444,"查询数据失败",null);
}
}
}
7.热部署
添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
在父工程pom文件中添加
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
开启自动构建
组合键
重启idea
8.消费者模块
9.工程重构
重复的部分
建立公共模块
MAVEN 执行clean和install操作
pom文件
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
五.Eureka服务注册与发现
1.服务治理
在传统的rpc远程调用框架中,管理服务与服务之间的依赖关系非常复杂;使用服务治理,实现服务调用,负载均衡,容错等,实现服务注册与发现。
2.服务注册与发现
Eureka采用cs设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中其他的微服务,使用Eureka的客户端连接到Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心,当服务器启动时,会把当前服务器的信息,例如通讯地址服务地址等以别名的方式注册到注册中心。另一方面(消费者|服务提供者),以别名的方式去注册中心获取实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架。核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系。在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信心)
Eureka系统架构
dubbo系统架构
3.单机Eureka构架步骤
新建工程
引入pom
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
yml文件
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名字
client:
register-with-eureka: false #表识不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
主配置类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
注册服务到服务端
8001服务
修改pom文件
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改yml
修改主配置类
80服务
4.Eureka集群
集群理论:
若注册中心只有一个,出现故障,会导致整个服务环境不可用,所以搭建Eureka注册中心集群,实现负载均衡和故障容错。
集群搭建:
搭建7002服务
修改映射配置
将订单服务8001微服务发布到eureka集群中
将80微服务发布到eureka集群中
8001服务提供者集群
新建8002
修改controller
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
@PostMapping(value = "/payment/create")
public CommonResult<Payment> create(@RequestBody Payment payment) {
int result = paymentService.create(payment);
log.info("插入结果: "+ result);
if(result>0){
return new CommonResult(200,"插入数据成功,端口号"+serverPort,result);
}else {
return new CommonResult(444,"插入数据失败,端口号"+serverPort,null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
log.info("查询结果: "+ payment);
if(payment!=null){
return new CommonResult(200,"查询数据成功,端口号"+serverPort,payment);
}else {
return new CommonResult(444,"查询数据失败,端口号"+serverPort,null);
}
}
}
80端口访问固定8001端口
修改为
访问
异常
原因:存在多个服务,机器不清楚如何调用
效果
5.actuator微服务信息完善
主机名称:服务名称修改
健康检查:
IP信息显示
6.服务发现Discovery
对于注册到eureka中的微服务,通过服务发现获取信息;
修改8001
@GetMapping(value = "/payment/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String e : services) {
log.info(e);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance s : instances) {
log.info(s.getServiceId() + "\t" + s.getHost() + "\t" + s.getPort() + "\t" + s.getUri());
}
return this.discoveryClient;
}
7.Eureka自我保护
概述:
保护模式主要是用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其注册列表中的信息,不再删除服务注册表中的数据。
表示Eureka进入保护模式
某个时刻某一个微服务不可用,Eureka不会立刻清理,依旧会对微服务的信息进行保存;属于CAP理论中的ap分支
禁止自我保护:(生产环境一般不会禁止)
7001
启动微服务
8001
关闭8001
服务超时,没有自我保护机制,服务被剔除
六.Zookeeper服务注册与发现
1.Eureka停止更新
2.springcloud整合Zookeeper代替Eureka
8004端口
写pom文件
<dependencies>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
写yml文件
写启动类
@SpringBootApplication
/*使用consul或者zookeeper作为注册中心时注册服务*/
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
写业务类
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/zk")
public String paymentzk(){
return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
启动后:
zookeeper节点为临时节点
80端口
<dependencies>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@SpringBootApplication
public class OrderZK80 {
public static void main(String[] args) {
SpringApplication.run(OrderZK80.class,args);
}
}
@Configuration
public class ApplicationContextConfig {
@Bean
/*赋予RestTemplate负载均衡的能力*/
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
@RestController
@Slf4j
public class OrderController {
public static final String INVOKE_URL = "http://cloud-provider-payment";
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/zk")
public String paymentInfo() {
return restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
}
}
访问
七.Consul服务注册与发现
consul分布式服务发现与配置管理系统,GO语言开发
consul提供一种完整的服务网络解决方案
提供微服务系统中的服务治理、配置中心、控制总线…
作用:
服务发现:提供http和dns两种发现方式
健康监测:
kv存储
多数据中心
可视化的web界面
2.安装consul
docker安装consul
3.使用consul
8006端口
pom
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
主启动类
@SpringBootApplication
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
业务类
@RestController@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/consul")
public String paymentInfo(){
return "springcloud with consul:"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
访问
80端口
问题需要解决:
访问
4.eureka、zookeeper、consul异同
CAP理论
c 强一致性 a 可用性 p 分区容错性
eureka 符合ap
consul、zookeeper 符合cp
八.Ribbon负载均衡服务调度
1.概述
基于Netflix Ribbon实现的一套客户端负载均衡的工具
在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动帮助你基于某种规则(简单轮询、随机连接)去连接这些机器。
2.作用:
负载均衡:
将用户的请求分发到多个服务上,达到系统的高可用;
(Nginx 服务器负载均衡,客户端所有请求会交给nginx,由nginx实现转发请求;
Ribbon本地负载均衡,在调用微服务接口时,会在注册中心获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术)
集中式负载均衡:即在服务的消费方和提供方之间使用独立的负载均衡设备(硬件F5 软件nginx),有设施负责把请求通过某种策略转发到服务的提供方
进程内负载均衡:将负载均衡逻辑集成到消费方,消费方通过注册中心获取哪些地址可用,然后从地址中选择一个合适的服务器。(Ribbon)
3.架构说明:
Ribbon工作时:
第一步 选择Eureka Server,优先选择在同一个区域,负载较少的server
第二步 根据用户指定的策略,从server服务注册列表选取一个地址。
Ribbon提供多种策略,包含轮询、随机、根据响应时间加权
4.使用:
RestTemplate
api
getForObject() 返回对象为响应体中数据转化成的对象,可理解为Json
getForEntity() 返回对象为ResponseEntity对象,包含响应中的一些重要信息,比如响应头、响应状态码、响应体
postForObject()
postForObject()
访问
5.Ribbon核心组件IRule:
IRule:根据特定的算法,从服务列表中选取一个要访问的对象
RoundRobinRule 轮询
RandomRule 随机
RetryRule 先按照RoundRobinRule 策略获取服务,若失败则在指定的时间内重试
WeightedResponseTimeRule 对RoundRobinRule 进行扩展,响应速度越快的服务选择 权重越大,越容易被选择
BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择并发量最小的服务
AvailabilityFilteringRule 先过滤故障实例,然后选择并发量较小的服务
ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和可用性选择服务器
替换负载均衡算法步骤:
修改80端口
需要新建一个包
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule();//定义为随机
}
}
6.Ribbon负载均衡算法:
源码:
RoundRobinRule
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
if (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
int nextServerIndex = this.incrementAndGetModulo(serverCount);
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
}
}
自定义负载均衡算法:
public interface LoadBalancer {
//收集服务器总共有多少台能够提供服务的机器,并放到list里面
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger=new AtomicInteger(0);
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
public final int getAndIncrement(){
int current;
int next;
do{
current=this.atomicInteger.get();
next=current>=Integer.MAX_VALUE?0:current+1;
}
while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("访问次数next值:"+next);
return next;
}
}
@RestController
@Slf4j
public class OrderController {
// public static final String PAYMENT_URL="http://localhost:8001";
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@Resource
private LoadBalancer loadBalancer;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/consumer/payment/create")
public CommonResult<Payment> create( Payment payment) {
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
@GetMapping(value = "/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getForEntity(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}
return new CommonResult<>(444,"操作失败");
}
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB(){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if(instances==null||instances.size()<=0){
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
}
访问:
九.OpenFeign服务接口调用
1.概述
(Feign被弃用)
OpenFeign
2.作用
总结:
OpenFeign 定义服务绑定接口,添加注解即可实现负载均衡服务调用
3.使用(微服务调用接口+@FeignClient)
pom
<!--openfeign-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
主启动类
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
业务类
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id);
}
@RestController
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
}
访问
4.OpenFeign超时控制
8001写暂停程序
/*暂停程序*/
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout(){
try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();}
return serverPort;
}
80端口
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
}
@RestController
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
/*openfeign-ribbon,客户端默认等待1秒钟*/
return paymentFeignService.paymentFeignTimeout();
}
}
访问http://127.0.0.1/consumer/payment/feign/timeout
解决方案:
yml中添加
#设置feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
#建立连接所用的时间,适合网络状况正常的情况下,两端连接所用时间
ReadTimeout: 5000
#建立连接后从服务器读取到可用资源的时间
ConnectTimeout: 5000
4.OpenFeign日志打印功能
日志级别:
NONE 默认不显示任何日志
BASIC 仅记录请求方法、url、响应状态码、执行时间
HEADERS 除了BASIC中定义的信息,还有请求和响应的头信息
FULL 除了Headers中定义的信息之外,包含请求和响应的正文和元数据
使用:
配置bean
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
yml
logging:
level:
#监控的接口
com.cz.springcloud.service.PaymentFeignService: debug
访问
后台显示:
十.Hystrix(服务降级,被弃用)
1.概念
作用:
服务降级
服务熔断
接近实时的服务监控
停更
2.重要概念
服务降级 fallback
服务器忙,请稍后再试,不让客户端等待并返回一个友好提示,fallback;
场景: 程序运行异常 超时 服务熔断触发服务降级 线程池/信号量打满导致服务降级
服务熔断 break
类似于保险丝达到最大访问后,直接拒绝访问,拉闸限电,然后调用服务降级中的方法并返回友好提示
服务限流flowlimit
秒杀高并发操作,严禁一窝蜂过来拥挤,排队一秒N个,有序进行
服务降级—服务熔断—恢复调用链路
3.案例
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.cz.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
server:
port: 8001
eureka:
client:
register-with-eureka: true #表识不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
# defaultZone: http://eureka7002.com:7002/eureka/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/
# server:
# enable-self-preservation: false
spring:
application:
name: cloud-provider-hystrix-payment
# eviction-interval-timer-in-ms: 2000
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
@Service
public class PaymentService {
//成功
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"正确" ;
}
//失败
public String paymentInfo_TimeOut(Integer id){
int timeNumber = 3;
/*超时*/
try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"错误"+" 耗时(秒)"+timeNumber;
}
}
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info("result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut(id);
log.info("result:"+result);
return result;
}
}
访问
演示:正确—错误—降级熔断—恢复
4.高并发测试
JMeter
访问
2个访问均响应慢
原因:tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
5.高并发 80加入
结果:
要么访问处于等待状态,要么消费端报超时错误
原因:
解决方法:
6.服务降级
降低配置 注解@HystrixCommand
(1)8001
设置自我调用超时时间的峰值,峰值内可正常运行
超过峰值,需要有兜底的方法处理,做服务降级fallback
业务类中
主启动类添加注解
访问
控制台
服务降级
(2)80
除了在服务提供端进行服务降级,客户端进行服务降级
yml
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
主配置类
访问
(3)问题
解决方案:
a.代码膨胀: 全局服务降级
通用和独享各自分开
程序运行报错、超时、服务熔断触发服务降级、线程池/信号量打满 时,配置@HystrixCommand,则采用就近原则读取fallback方法,当方法上没有配置降级方法,则读取调用类上配置的降级方法。
b.和业务逻辑混淆
为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
配置降级,在service层配置@FeignClient属性fallback
7.服务熔断
(1)概述:
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路上的某个微服务出错或响应时间太长时,会进行服务降级,进而熔断此节点微服务的调用,快速返回错误的响应信息。当检测到此节点微服务调用正常后,恢复调用链路。
在Spring Cloud框架中,熔断机制通过Hystrix实现,Hystrix会监控微服务间的状况,当失败的调用到一定的阈值,缺省是5秒内20次调用失败,会启动熔断机制,熔断机制的注解@HystrixCommand
(2)案例:
8001
配置的参数的源头:
无异常访问 http://127.0.0.1:8001/payment/circuit/1
异常访问 http://127.0.0.1:8001/payment/circuit/-1
在规定时间范围内,请求次数超过规定请求次数,异常访问 失败率达到规定失败率,触发熔断;服务降级
之后恢复调用链路
(3)原理:
熔断打开: 请求不再调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设置时钟则进入半熔断状态;
熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则,则认为当前服务恢复正常,关闭熔断
熔断关闭:熔断关闭则不会对服务进行熔断
断路器开启或关闭的条件:
当默认时间内请求满足一定的阈值(默认10秒内超过20次请求),当失败率达到一定的时候(默认10秒内超过50%的请求失败),断路器就开启。当开启的时候,所有请求都不会再进行转发。一段时间后(默认为5秒),这个时候断路器处于半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。
断路器打开之后:
再有请求调用的时候,将不会调用主逻辑,而是直接调用fallback或设置在类上的defaultFallback或FeignClient中配置的fallback.通过断路器,实现自动发现错误,并将降级逻辑切换为主逻辑,减少延迟的效果
恢复原来的主逻辑:
当断路器打开,对主逻辑进行熔断后,hystrix会启动一个休眠时间窗,在这个时间窗,降级逻辑成为临时的主逻辑,当休眠时间窗到期,断路器将会进入半开状态,释放一次请求到原来的主逻辑,如果此次请求正常返回,则断路器闭合,主逻辑恢复。若请求出现问题,断路器继续打开,休眠时间窗重新计时。
(4)参数说明:
@HystrixCommand
// HystrixCommand 命令所属的组的名称:默认注解方法类的名称
String groupKey() default "";
// HystrixCommand 命令的key值,默认值为注解方法的名称
String commandKey() default "";
// 线程池名称,默认定义为groupKey
String threadPoolKey() default "";
// 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
String fallbackMethod() default "";
// 配置hystrix命令的参数
HystrixProperty[] commandProperties() default {};
// 配置hystrix依赖的线程池的参数
HystrixProperty[] threadPoolProperties() default {};
// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。我们也可以通过此方法定义哪些需要忽略的异常
Class<? extends Throwable>[] ignoreExceptions() default {};
// 定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
// 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
HystrixException[] raiseHystrixExceptions() default {};
// 定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
String defaultFallback() default "";