SpringCloud阶段总结
文章目录
练习项目环境准备
构建父工程
- 选用的spring-cloud的版本是Hoxton.SR1,springboot的版本是2.2.2.RELEASE,springcloudalibaba的版本是2.1.0.RELEASE
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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.18.24</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.spring.boot.starter>1.1.22</druid.spring.boot.starter>
<mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
<spring.boot.dependencies.version>2.2.2.RELEASE</spring.boot.dependencies.version>
<spring.cloud.alibaba.dependencies.version>2.1.0.RELEASE</spring.cloud.alibaba.dependencies.version>
<spring.cloud.dependencies.version>Hoxton.SR1</spring.cloud.dependencies.version>
</properties>
- 在父工程中加入dependencyManagement(依赖管理器),dependencyManagement可以将项目中用到的maven地址进行统一的管理,保证子项目中的maven坐标的版本号一致
- 注意在子项目中仍然需要引入manven坐标
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter}</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.dependencies.version}</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>${spring.cloud.alibaba.dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 加入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>
服务注册
- 将所有微服务到服务注册中心,进行统一的管理
Eureka
使用步骤
- 创建eureka微服务
- 导入eureka maven坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- 编写eureka yml配置文件
server:
port: 7001
spring:
application:
name: eureka-service
eureka:
instance:
hostname: localhost # 配置eureka地址
client:
#不向注册中心注册自己
register-with-eureka: false
#自己是注册中心,不用获取注册表
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: true # 开启/关闭自我保护
- 在服务端启动类上添加@EnableEurekaServer注解
- 在微服务添加eureka客户端maven坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- 编写yml配置文件
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:7001/eureka/
# #不向注册中心注册自己
注意: defaultZone在编写时没有智能提示,当时我在运行项目时,因为字母拼写错误,并且直接复制到另一个微服务项目中,结果两个都无法运行,最后才发现是拼写错误,在此记录一下那段不堪回首的岁月。
- 在启动类上添加@EnableEurekaClient、@EnableDiscoveryClient注解,
第一个注解作用是,启动eureka的客户端,第二个注解时可以获取在eureka中注册的服务信息(在后面的ribbon的使用中会进行描述)
服务调用
- 两种方式,一种是restTemplete+Ribbon实现服务的调用
- 另一种是使用springcloude官方推荐的openfeign进行服务的远程调用
RestTmplete+Ribbon
- 使用方法简单, 在配置类中注入RestTemplete,并且添加**@LoadBalanced**注解
- 使用testtemplete注解进行服务的调用,即将IP地址与端口替换成服务的名称
- 如何被调用服务中是微服务集群,则默认采用轮询算法(Round)
负载均衡
- Ribbon是服务器内的负载均衡,即选择访问哪个微服务项目,Nginx是实现服务器端的负载均衡,决定访问哪个服务器
- 实现负载均衡算法的替换
- 在启动类所在包之外新建配置文件(将配置文件放在@CompentSacn注解扫描不到的包下方),可以实现对特定微服务的定制化算法更换
- 在Myribbonrule配置类中,注入Irule实现类,添加@Configuration注解
@Configuration
public class Myribbonrule {
@Bean
public IRule iRule(){
return new RandomRule();
}
}
- Ribbon内置的负载均衡策略
- 手写Ribbon的轮询算法
- 实现原理:此时请求的次数%该微服务集群的数量=本次请求第几个微服务
@GetMapping("getport")
public R getport() {
String url = "http://PAYMENT-SERVICE/port";
List<ServiceInstance> instancesById = discoveryClient.getInstances("PAYMENT-SERVICE");
ServiceInstance choicceservice = loaeBalance.choicceservice(instancesById);
// return new R(200,choicceservice.getUri().toString(),null);
ResponseEntity<R> forEntity = restTemplate.getForEntity( choicceservice.getUri()+"/port", R.class);
// System.out.println(instancesById);
return forEntity.getBody();
//return
}
public interface LoaeBalance {
ServiceInstance choicceservice(List<ServiceInstance> instancesById);
}
@Component
public class LoadBalanceiml implements LoaeBalance {
// 原子类实现整型的加加减减操作
private AtomicInteger atomicInteger;//代表的是次数
public LoadBalanceiml() {
this.atomicInteger = new AtomicInteger(0);
}
// 获取第几次请求
int getcurrent(int count) {
int current;
int next;
do {
current = atomicInteger.get();//获取此时是第几次请求
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current, next)); //两者相等的时候跳出循环,保证线程安全
return next;
}
@Override
public ServiceInstance choicceservice(List<ServiceInstance> instancesById) {
if (instancesById==null || instancesById.size()==0){
return null;
}
int count=instancesById.size();
// 获取到取第几个服务实例
int next = getcurrent(count);
//
next=next%count;//取余数获取列表下标
return instancesById.get(next);
}
}
OpenFeign
- 添加openfeign的pom坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!------feign连接池坐标----->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 在启动类上添加@EnableFeignClients注解
- 新建feign接口,添加@FeignClient注解,
@FeignClient(value = "PAYMENT-SERVICE",configuration = FeignConfiogure.class)
public interface Feighdao {
// 增加流水
@PostMapping
public R addcontroller(@RequestBody Payment payment);
@GetMapping("port")
public R<String> getport();
// 增加流水
@GetMapping("{id}")
public R<Payment> getpayment(@PathVariable(value = "id") long id); //类路径添加值
注意如何使用@pathvariable注解时,需要将参数名称写入value参数中,@PathVariable(value = “id”) long id否则会报错,血与泪的教训。
- 基于注解的方式添加日志级别
public class FeignConfiogure {
// 修改日志级别
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
- 配置yml文件
feign:
client:
config:
PAYMENT-SERVICE:
loggerLevel: BASIC
# 连接超时时间,默认2s,设置单位为毫秒
connectTimeout: 2000
# 请求处理超时时间,默认5s,设置单位为毫秒。
readTimeout: 5000
httpclient:
# 最大连接池数
max-connections: 200
# 单路径最大连接池数
max-connections-per-route: 50
- 基于配置文件修改feign的日志级别可以针对单个服务:
feign:
client:
config:
userservice: # 针对某个微服务的配置
loggerLevel: FULL # 日志级别
也可以针对所有服务:
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
而日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
GateWay
- maven地址
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- yml配置
gateway:
discovery:
locator:
enabled: true
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
- id: payment-nacos-service
uri: lb://payment-nacos-service
predicates:
- Path=/payment/**
- 注意
在GateWay项目中需要取消springweb依赖,否则会报错 - 全局配置,需要实现 Ordered, GlobalFilter接口,getOrder()代表优先级,order越小,优先级越高
@Component
public class Gloableconfigure implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(org.springframework.web.server.ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("全局过滤器生效");
// 不符合过滤器条件,直接返回
// return exchange.getResponse().setComplete();
exchange.getRequest().getHeaders().add("test","ceshi");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
Nacos
服务注册中心
- mavne依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- yml配置文件
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
- 添加注解
@EnableDiscoveryClient
服务配置中心
- maven地址
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<!-- <version>2.2.2.RELEASE</version>-->
</dependency>
- yml配置文件
需要新建一个bootstrap.yml文件放置一下内容
spring:
cloud:
nacos:
discovery:
# enabled: true
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
group: dev
namespace: 91c1ae59-32ee-4f0f-b58b-a5650ca95740
- dataID组成
服务名称-环境.文件地址(支持yaml文件,但不支持yml文件)
application.name-profile.active.文件地址 - 新建组别(group)
只能访问同组内的yaml文件,必须在配置文件中指定 - 新建命名空间(namespace)
新建命名空间,必须在配置文件中指定