SPRINGCLOUD学习篇
- 一、SPRINGCLOUD整合Eureka
- 创建一个父工程
- 跳过单元测试
- 二、cloud-provider-payment8001
- 三、cloud-consumer-order80
- 三、cloud-api-commons
- 整体架构
- 四、Eureka 服务注册与发现
- 单机版本Eureka架构
- 五、Eureka集群搭建
- 六、配置8001与80项目注册到Eureka集群中
- 七、搭建提供者集群
- 八、DiscoveryClient对象的使用
- Eureka自我保护机制
- 什么是自我保护模式?
- 九、Zookeeper服务注册与发现
- 十、cloud-provider-payment8004
- 十一、cloud-consumerzk-order80
- 十二、Consul服务注册与发现
- 十三、创建服务提供者
- 十四、创建服务消费者
- 十五、总结三种注册中心
- 十六、Ribbon负载均衡服务调用
一、SPRINGCLOUD整合Eureka
创建一个父工程
项目名称:cloud2020
编写POM文件
pom.xml如下:
<?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.cloud</groupId>
<artifactId>cloud2020</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 统一管理jar包版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.coompiler.targer>1.8</maven.coompiler.targer>
<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>
<!-- dependencyManagement 子模块继承后,提供作用:锁定版本+子module不用写groupID和version-->
<dependencyManagement>
<dependencies>
<!-- springboot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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>
<!-- mysql connector-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- druid 数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis框架-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!-- juint-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<!-- lombokjar包依赖-->
</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>
- Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。
- 通常会在一个组织或者项目的最顶层的父pom中可以看到dependencyManagement元素。
- 使用pom.xml中的dependencyManagement元素能让所有在子项目中引用一个依赖而不用显示的列出版本号。
- Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后他就会使用这个dependencyManagement元素中指定的版本号。
- dependencyManagement 对jar版本统一管理 ,只做声明,并不会引入。
总结:这样做的好处就是:如果有多个子项目都引用用一个依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个项目的修改;另外如果某个子项目需要另外的一个版本,只需要声明version即可。
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
如果不存在子项目中声明依赖,是不会从父项目中继承下来的,只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom
如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
跳过单元测试
设置跳过测试类
二、cloud-provider-payment8001
1、创建一个消费者来消费消息
2、创建一个生产者来生产消息
如图所示:
项目名称:cloud-provider-payment8001
1、创建项目
创建微服务提供者支付Module模块
第一步:
第二步:
第三步:
2、 修改POM文件
<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>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
3、修改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://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
#配置mybatis
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.cloud.springcloud.entity
4、创建主启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
5、业务逻辑开发
1、创建数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for payment
-- ----------------------------
DROP TABLE IF EXISTS `payment`;
CREATE TABLE `payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`serial` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of payment
-- ----------------------------
BEGIN;
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
2、创建实体对象
类名称为:Payment
package com.cloud.springcloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
封装一个结果对象
类名称为:commonResult
package com.cloud.springcloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult <T>{
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
3 、编写dao接口
接口名称为:PaymentDao
package com.cloud.springcloud.dao;
import com.cloud.springcloud.entity.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface PaymentDao {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
4、编写映射文件
在resource目录下新建一个mapper文件夹
在mapper文件夹中新建一个PaymentMapper.xml文件
在PaymentMapper.xml文件中编写新增,查询sql语句
编写:PaymentMapper.xml
<?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.cloud.springcloud.dao.PaymentDao">
<!--新增语句-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
</insert>
<!--结果集映射处理-->
<resultMap id="BaseResultMap" type="com.cloud.springcloud.entity.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<!--查询语句-->
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" >
select * from payment where id=#{id};
</select>
</mapper>
5、编写业务逻辑层
1、编写service接口
创建一个PaymentService接口类
package com.cloud.springcloud.service;
import com.cloud.springcloud.entity.Payment;
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(Long id);
}
2、编写service实现类
创建一个PaymentServiceImpl类
package com.cloud.springcloud.service.impl;
import com.cloud.springcloud.dao.PaymentDao;
import com.cloud.springcloud.entity.Payment;
import com.cloud.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@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);
}
}
6、编写控制层Controller
创建一个PaymentController类
package com.cloud.springcloud.controller;
import com.cloud.springcloud.entity.CommonResult;
import com.cloud.springcloud.entity.Payment;
import com.cloud.springcloud.service.PaymentService;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@PostMapping(value = "/payment/create")
public CommonResult create(Payment payment){
int result=paymentService.create(payment);
log.info("*****插入结果"+result);
if(result>0){
return new CommonResult(200,"插入数据库成功",result);
}else {
return new CommonResult(201,"插入数据库失败",null);
}
}
@GetMapping(value="/payment/get/{id}")
public CommonResult 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(201,"没有找到对应记录,查询ID!"+id,null);
}
}
}
7、测试结果
查询结果:
测试地址:http://localhost:8001/payment/get/1
新增结果:
测试地址为:http://localhost:8001/payment/create
6、开启热部署项目
1、在pom.xml文件中添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
添加maven插件
在父工程中添加maven插件
<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
三、cloud-consumer-order80
1、创建项目
项目名称:cloud-consumer-order80
微服务消费者订单Module模块
第一步:创建订单项目 cloud-consumer-order80
2、修改pom.xml文件
<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>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>
3、编写YML文件
在resource文件下建一个application.yml文件
编写配置,内容如下:
#编写项目端口号
server:
port: 80
spring:
application:
name: cloud-order-service
4、编写主启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
5、编写实体类
类名为:Payment
package com.cloud.springcloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Payment implements Serializable {
private Long id;
private String serial;
}
类名称为:commonResult
package com.cloud.springcloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
private T data;
public CommonResult(Integer code,String message){
this(code,message,null);
}
}
6、编写控制层
package com.cloud.springcloud.controller;
import com.cloud.springcloud.entity.CommonResult;
import com.cloud.springcloud.entity.Payment;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL="http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@PostMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
log.info("*****新增记录:"+payment);
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id")Long id){
log.info("*****查询记录:"+id);
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
7、编写一个配置类
类名为:ApplicationContextConfig
package com.cloud.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
8、测试结果:
测试查询地址:http://localhost/consumer/payment/get/1
测试新增地址:http://localhost/consumer/payment/create?serial=aaaddcaac
如果出现值插入为null,修改cloud-provider-payment80中控制层新增接口,添加一个@RequestBody 如图所示:
三、cloud-api-commons
1、创建项目
第一步:
第二步:
2、修改pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
3、进行项目打包
cloud-api-commons 打包给其他两项目进行使用
打包方式如图所示:
4、删除项目中实体对象
cloud-provider-payment8001
cloud-consumer-order80
具体操作,如图所示:
cloud-provider-payment8001
cloud-consumer-order80
实体类删除后两项目都报错。
需要给两个项目进行添加依赖
具体操作如下:
cloud-provider-payment8001
cloud-consumer-order80
<dependency>
<groupId>com.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
5、测试项目
查询结果:
查询地址:http://localhost/consumer/payment/get/1
新增地址:http://localhost/consumer/payment/create?serial=aaad3333
整体架构
架构设计图
整体架构图如下:
四、Eureka 服务注册与发现
Eureka 服务的治理
Eureka 采用了CS架构模型,Eureka Server做为服务的注册功能的服务器,它是注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接,这样系统的维护人员就可以通过Eureka Server 来监控系统中各个微服是否正常运行。
在服务注册与发现中,有一个注册中心,当服务器启动的时候,会把当前自己服务的信息 比如(服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)
在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址)
)
Eureka包含两个组件:
Eureka Server
提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中可以直观的看到。
Eureka Client
通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server 在多个心跳周期内没有接收到某个节点心跳 Eureka Server 将会从服务注册列表中把这个服务节点移除(默认90秒)
单机版本Eureka架构
1、创建项目 cloud-eureka-server7001
创建项目如图所示:
2、修改POM.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</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>
</dependency>
<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>
3、编写YML配置文件
创建在resource下创建一个application.yml文件
#编写服务端口
server:
port: 7001
#编写服务名称
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、编写主启动类
类名称为:EurekaMain7001
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
5、测试结果
测试地址:http://localhost:7001/
6、修改服务(8001/80项目)
修改以下服务:
cloud-provider-payment8001项目
cloud-consumer-order80项目
具体修改如下:
第一步:
在cloud-provider-payment8001 的pom中添加依赖
cloud-provider-payment8001
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
第二步:
在cloud-provider-payment8001 的application.yml文件修改
#Eureka 客户端配置
eureka:
client:
#表示是否将自己注册进Eureka Server中去,默认是true。
register-with-eureka: true
#是否从Eureka Server 抓取已有的注册信息,
#默认为:true 但节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
第三步:
在cloud-provider-payment8001 的主启动类添加注解
第四步:
进行项目启动测试:
cloud-consumer-order80
第一步:
给cloud-consumer-order80 的pom文件中添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
第二步:
修改cloud-consumer-order80 中application.yml文件
#Eureka配置
eureka:
client:
#表示是否将自己注册进Eureka Server中 默认为true
register-with-eureka: true
#是否从Eureka Server抓取已有注册信息,默认为true 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
第三步:
修改cloud-consumer-order80 中的主启动类添加注解
如图所示:
第四步:
重启项目查看是否注册成功!
查看地址:http://localhost:7001/
五、Eureka集群搭建
解决单点故障,实现高可用
解决方案:搭建Eureka注册中心集群,实现负载均衡+ 故障容错。
参考项目cloud-eureka-server7001
创建项目cloud-eureka-server7002
1、cloud-eureka-server7002
2、修改POM.xml
可以直接拷贝cloud-eureka-server7001中pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</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>
</dependency>
<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>
3、编写YML文件
在resource文件下 新建一个application.yml
并添加如下内容:
由于localhost多个项目会出现重名的现象所以需要做映射处理
修改host文件
mac系统 sudo vim /etc/hosts
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
第一步:先修改7001中的yml文件
内容如下:
#编写服务端口
server:
port: 7001
#编写服务名称
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7002.com:7002/eureka/
第二步编写7002 application.yml文件
#编写服务端口
server:
port: 7002
#编写服务名称
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/
4、编写主启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7002.class,args);
}
}
5、测试结果
查看是否有互相注册
如图所示:7001中有7002
7002中有7001
六、配置8001与80项目注册到Eureka集群中
1、具体配置
第一步:修改cloud-provider-payment8001 中application.xml文件
内容如下:
修改defaultZone
的值
#Eureka 客户端配置
eureka:
client:
#表示是否将自己注册进Eureka Server中去,默认是true。
register-with-eureka: true
#是否从Eureka Server 抓取已有的注册信息,默认为:true 但节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版本
第二步:修改cloud-consumer-order80 中application.yml文件
内容如下:
修改位置:defaultZone的值
#Eureka配置
eureka:
client:
#表示是否将自己注册进Eureka Server中 默认为true
register-with-eureka: true
#是否从Eureka Server抓取已有注册信息,默认为true 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
2、测试结果
重启项目:
7002服务
结果如图所示:
测试结果7001
如图所示:
七、搭建提供者集群
参考:cloud-provider-payment8001
创建:cloud-provider-payment8002
如图所示:
1、创建项目: cloud-provider-payment8002
2、修改pom文件
可以直接拷贝cloud-provider-payment8001 pom依赖
<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>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
3、编写YAML文件
在resource文件下新建一个application.yml 文件
可以直接复制cloud-provider-payment8001 中的application.yml文件内容
1、修改端口号
修改Spring.application.name值
如图所示:
#服务端口
server:
port: 8002
#服务名称
spring:
application:
name: cloud-payment-service
#数据源的配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
#配置mybatis
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.cloud.springcloud.entity
#Eureka 客户端配置
eureka:
client:
#表示是否将自己注册进Eureka Server中去,默认是true。
register-with-eureka: true
#是否从Eureka Server 抓取已有的注册信息,默认为:true 但节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版本
4、编写启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8002 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8002.class,args);
}
}
5、编写实体类,接口
dao,mapper,service,controller 全部从cloud-provider-payment8001中直接复制过来即可。
mapper复制如图:
controller,service,dao复制如图:
6、服务调用测试
测试方法:
1、在cloud-provider-payment8001 控制层打印出当前服务的端口,来判断是调用的那台服务。
添加内容如下:
2、在cloud-provider-payment8002 控制层打印出当前服务的端口,来判断是调用的那台服务。
添加内容如下:
7、修改cloud-consumer-order80项目
单机版本编写时调用是写死的,所以需要进行修改。
如图所示:端口是写死的所以需要修改
1、将PAYMENT_URL的值修改成服务名次:
如图所示服务名称:
修改完成后如图所示:
2、由于是用的服务的名称,调用服务时也不知道那台服务来处理请求,所以这里就出来来负载均衡配置
修改cloud-consumer-order80项目中ApplicationContextConfig类
修改如图所示:并通过 @LoadBalanced 注解开启客户端 负载均衡。
8、进行测试服务调用
测试结果如图所示:
注意查看端口号的变化
9、完善显示设置
1、主机名称修改:
2、访问信息有IP信息提示
主要修改8001 与8002 两台服
分别在cloud-provider-payment8001, cloud-provider-payment8002 中修改application.yml
在此文件中加入如图所示:
#Eureka 客户端配置
eureka:
client:
#表示是否将自己注册进Eureka Server中去,默认是true。
register-with-eureka: true
#是否从Eureka Server 抓取已有的注册信息,默认为:true 但节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版本
#添加主机名称
instance:
instance-id: payment8001
修改后结果如图显示:
同上修改cloud-provider-payment8002
如图所示:
设置访问IP显示:
同样在YAML文件中添加以下配置,如图所示:
10、最终结果:
如图所示:
八、DiscoveryClient对象的使用
在这里用cloud-provider-payment8001为列:
1、在cloud-provder-payment8001中PaymentController类里引用此对象
2、编写一个方法用来测试DiscoveryClient 对象获取服务列表
如图所示:
1、测试方法编写
@GetMapping(value = "/payment/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String element:services){
log.info("*******element:"+element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance:instances){
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
2、添加注解
在主启动类上添加@EnableDiscoveryClient
此注解
通过@EnableDiscoveryClient注解 让该应用注册为 Eureka 客户端应用, 以获得服务发现的能力
如图所示:
3、接口测试
测试地址:http://localhost:8001/payment/discovery
测试结果如图所示:
网页显示结果:
后台日志显示的结果:
Eureka自我保护机制
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer
不立刻将
EurekaClint服务从服务列表中删除。
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销实例(默认90秒)但是当网络分区故障发生(延时,卡顿,拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了–应为微服务本身其实是健康当,
此时本不应该注销这个微服务
。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时内丢失过多客户端时(可能发生来网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureka Server 会保护服务注册表中当信息,不会注销任何服务实例,使用自我保护模式,可以让Eureka集群更加当健壮,稳定
具体配置如下:
#Eureka 客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
eureka.instance.lease-renewa l-interval-in-seconds= 30
#Eureka 服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时剔除服务。
eureka.instance.lease-expiration-duration-in-seconds= 90
九、Zookeeper服务注册与发现
Zookeeper 下载
通过wget下载zookeeper
#第一步:先安装wget 命令如下:
yum -y install wget
#第二步:安装好wget后,通过wegt 进行下载zookeeper安装包 如果地址失效去官方找:https://zookeeper.apache.org/releases.html
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
#第三步:通过ls或者ll命令查看有没有下载成功,下载成功则用如下命令进行解压
tar -xvf apache-zookeeper-3.5.8-bin.tar.gz #下载的zookeeper压缩包文件名称
十、cloud-provider-payment8004
1、创建项目
项目名称:cloud-provider-payment8004
创建方式如图所示:
2、修改POM文件
pom文件可以直接复制8001或者8002项目中的pom文件
需要替换一个jar包依赖,把Eureka依赖修改为 zookeeper依赖
具体pom内容如下:
<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>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- zookeeper jar包依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
</dependencies>
3、编写YAML文件
在resource文件下创建一个YAML文件
文件名称为:application.yml
编写内如下所示:
#编写服务端口号
server:
port: 8004
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
#zookeeper服务地址配置
connect-string: 10.168.8.100:2181
4、编写启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
5、编写业务类
controller类
package com.cloud.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@Slf4j
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/zk")
public String paymentzk(){
return "springcloud with zookeeper "+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
6、启动zookeeper服务
#进入zookeeper的bin目录
cd bin
#启动zookeeper服务
sh zkServer.sh start #或者使用 ./zkServer.sh
#Error 错误信息
Error: JAVA_HOME is not set and java could not be found in PATH.
#解决方案,安装JDK 通过wget下载 jdk 命令如下
wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u141-b15/336fa29ff2bb4ef291e347e091f7f4a7/jdk-8u141-linux-x64.tar.gz"
#解压jdk 命令如下
tar -xvf jdk-8u162-linux-x64.tar.gz
#配置环境变量 先进入jdk的bin目录
cd jdk1.8.0_162/bin
#查看当前目录地址 命令
pwd
#复制目录地址:/usr/local/src/jdk1.8.0_162/bin
#修改环境变量 命令如下
vim /etc/profile
# 按键盘i字母进行编辑模式
i
#通过上下键光标移动到文件末尾,编辑如下值
export JAVA_HOME=/usr/local/src/jdk1.8.0_162
export PATH=${PATH}:${JAVA_HOME}/bin
#编辑好后,按esc进行退出编辑模式 ,进行保存 按住shift+:输入一下值wq 按回车键进行保存
wq
#重新加载配置文件
source /etc/proflie
#查看是否配置成功,输入查询版本号命令
java -version
#显示了版本号说明配置成功!如图所示
重新启动zookeeper
sh zkServer.sh start #或者 ./zkServer.sh start
查看zookeeper启动状态
sh zkServer.sh status
以上表示启动失败,我们需要修改一下配置文件,
#在zookeeper根目录下面配置创建两个文件夹data ,log
mkdir data log
#进入创建好到data目录复制一下路径
cd data
pwd
#/usr/local/src/apache-zookeeper-3.5.8-bin/data
#然后进zookeeper 配置文件夹下修改zoo.cfg
cd ../conf/
#复制一份配置文件
cp zoo_sample.cfg zoo.cfg
#修改配置文件
#修改dataDir路径
dataDir=/usr/local/src/apache-zookeeper-3.5.8-bin/data
#添加dataLogDir配置
dataLogDir=/usr/local/src/apache-zookeeper-3.5.8-bin/log
#最后保存 重新启动zookeeper 启动成功如图所示
7、关闭防火墙
如图所示:表示在尝试进行连接
用一下方法进行关闭:
sudo systemctl stop firewalld 临时关闭
sudo systemctl disable firewalld ,然后reboot 永久关闭
sudo systemctl status firewalld 查看防火墙状态。
8、查看zookeeper注册情况
连接zookeeper命令
# 在zookeeper 的bin目录下
sh zkCli.sh
#查看注册的服务
ls /
#查看注册信息ID
ls /services/cloud-provider-payment
十一、cloud-consumerzk-order80
1、创建项目
项目名称:cloud-consumerzk-order80
2、修改pom文件
注意事项:可以直接从cloud-consumer-order80项目中复制pom文件,只需要修改Eureka jar包依赖换成zookeeper jar包依赖
<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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</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.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
3、编辑YAML文件
在resource资源文件下创建一个application.yml文件
server:
port: 80
spring:
application:
name: cloud-consumer-order
cloud:
#注册到zookeeper地址
zookeeper:
connect-string: 10.168.8.100:2181
4、编写主启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class OrderZKMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderZKMain80.class,args);
}
}
5、编写业务类
1、配置类编写
package com.cloud.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
2、编写Controller
package com.cloud.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class OrderZKController {
public static final String INVOKE_URL="http://cloud-provider-payment";
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/zk")
public String paymentInfo(){
String result =restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
return result;
}
}
6、测试结果
十二、Consul服务注册与发现
Consul
是一套开源的分布式服务器发现和配置管理系统,是由HashiCorp公司使用Go语言开发
。
提供了微服务系统中的服务治理,配置中心,控制总线等功能,这些功能,这些功能中的每个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
它具有很多优点,包括:基于raft协议,比较简洁;支持健康检查,同时支持HTTP和DNS协议,支持跨数据中心的WAN集群,提供图形界面,跨平台,支持Linux ,mac,Windows。
1、Spring cloud Consul 具有一下特性
Service Discovery (服务的发现)
Health Checking (健康监测)
KV Store (KV存储)
Secure Service Communication (多数据中心)
Multi Datacenter (可视化Web界面)
2、consul下载地址
根据自己的电脑系统类型进行下载即可:
官方下载地址:https://www.consul.io/downloads
3、consul启动命令
consul agent -dev
#如果不清楚可以输入
consul -h
查看consul图形化页面,consul启动后在浏览器中输入如下地址:localhost:8500
consul 开启的默认访问端口
是8500
十三、创建服务提供者
1、创建cloud-provider-payment8006
如图所示:
2、修改pom.xml文件
<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>
<!-- consul jar依赖引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3、编写YAML文件
在resource目录下创建一个application.yml文件
server:
port: 8006
spring:
application:
name: cloud-provider-payment
#cloud-consul配置
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
4、编写主启动类
package com.cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
5、编写业务类
控制层Controller
package com.cloud.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/consul")
public String paymentConsul(){
return "springcloud with consul"+ serverPort+"\t"+ UUID.randomUUID().toString();
}
}
6、测试结果
访问结果
注册结果
十四、创建服务消费者
1、创建cloud-consumer-consul-order80
2、修改pom文件
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</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>
</dependencies>
3、编写YMAL文件
在resource文件夹下面创建一个application.yml文件
server:
port: 80
spring:
application:
name: cloud-consul-order
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
4、编写主启动类
package cloud.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class,args);
}
}
5、编写配置类
package cloud.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
6、编写业务逻辑
编写controller类
package cloud.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderConsulController {
private static final String INVOKE_URL="http://consul-provider-payment";
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/consul")
public String paymentInfo(){
return restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
}
}
7、查看测试结果
1、查看注册结果如图所示:
2、查看访问结果如图所示:
十五、总结三种注册中心
1、CAP原则
定义:CAP原则又称
CAP定理
,指的是在一个分布式系统中、Consistency (一致性
)、Availability (可用性
)、Partitiontolerance(分区容错性
),三者不可兼得。
一致性(C):
在分布式系统中的所有数据备份,在同一时刻是否同样的值。(例如:取一个值不能这会是a,过一会变成了b,想要确保数据一致性必须使用事务,而事务在分布式系统里很难实现。)
可用性(A):
在集群中一部分节点故障后,集群整体是否还能响应客户的读写请求。(可用性一般使用负载均衡或者心跳检测加IP漂移来保证)
分区容忍性(P):
分区容错性是指系统能够容忍节点之间的网络的故障。也就是说单台服务器或多台服务器出现网络问题后,其他服务依然能够正常提供服务。分区容忍性可以使用选举机制实现。
三种取舍策略:
CA:(一致性与可用性):
满足了一致性
与可用性
那么就不能满足分区容忍性
,这样也就意味着放弃了系统的可扩展性了,也就是说分布式节点受到限制,没办法部署子节点,这样就违背了设计分布式架构的初衷了。
CP:(一致性与分区容忍性):
满足了一致性
与分区容忍性
那么就不能满足可用性,也就是说要保持强一致性,每个请求访问服务器之间需要保持强一致性,而由于P是分区容忍性所以会导致同步时间无限延长(也就是说要等服务器之间数据全部同步完后才能访问服务器) ,一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验感了。等待所有的数据全部一致后再让用户才能正常访问系统,这种设计CP原则系统其实不少,最为典型的分布式数据库,如Redis,HBase等,对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库就好,没必要再浪费资源来部署分布式数据库。
AP:(可用性与分区容忍性):
要高可用并允许分区,则需要放弃一致性,一旦分区的产生,那么节点之间有可能会产生失联的情况。为了高可用只能用本地数据来提供服务,而这样会导致全局数据的不一致性。典型的应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不至于造成用户购物流程的严重阻塞。
2、三种注册中心区别
组件 | 语言 | CAP | 服务键康检查 | 对外暴露接口 | Spring Cloud集成 |
---|---|---|---|---|---|
Eureka | java | AP | 可配置支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
Zookeeper | java | CP | 支持 | 客户端 | 已集成 |
十六、Ribbon负载均衡服务调用
1、Ribbon的定义
Ribbon是Netflix发布的开源项目,主要功能是提供
客户端的软件负载均衡算法和服务调用。
Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer (简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
2、LB负载均衡(Load Balance)是什么?
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
常见的负载均衡软件Nginx,LVS,硬件F5等
3、Ribbon与Nginx 服务端负载均衡区别?
Nginx是服务器负载均衡,客户端所有请求都会给nginx实现转发请求。即负载均衡是由服务端实现端。
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程调度技术。
4、手写负载均衡业务
在 cloud-consumer-order80 项目中springcloud包下创建一个lb包,在包中创建一个接口,命名为:LoadBalancer
在LoadBalancer接口中编写一个方法 ,代码如下:
package com.cloud.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
public interface LoadBalancer {
ServiceInstance instance(List<ServiceInstance> serviceInstances);
}
再在lb包中创建一个 LoadBalancer接口的实现类,命名为:LoadBalancerImpl 重写instance方法
具体内容如下:
package com.cloud.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class LoadBalancedImpl implements LoadBalancer{
private AtomicInteger atomicInteger= new AtomicInteger(0);
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);
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> serviceInstances) {
int index= getAndIncrement()%serviceInstances.size();
return serviceInstances.get(index);
}
}
AtomicInteger类
中主要实现了整型的原子操作,防止并发情况下出现异常结果,其内部主要依靠JDK 中的unsafe 类操作内存中的数据来实现的。volatile 修饰符保证了value在内存中其他线程可以看到其值得改变。CAS操作保证了AtomicInteger 可以安全的修改value 的值。