一、注册中心
Eureka
CAP定理: Eureka是AP (高可用性,分区容错性)
Eureka由多个instance(服务实例)组成,这些服务实例可以分为两种:Eureka Server
和Eureka Client
。为了便于理解,我们将Eureka client再分为Service Provider和Service Consumer。
- Eureka Server 提供服务注册和发现
- Service Provider 服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到
- Service Consumer 服务消费方,从Eureka获取注册服务列表,从而能够消费服务
配置Eureka服务端
搭建Eureka Server 需要引入依赖
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
配置yml信息
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#集群指向其它eureka
defaultZone: http://localhost:7001/eureka/
#单机就是7001自己
# defaultZone: http://localhost:7002/eureka/
#server:
#关闭自我保护机制,保证不可用服务被及时踢除
#enable-self-preservation: false
#eviction-interval-timer-in-ms: 2000
启动类信息
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
搭建Eureka的服务提供方
需要引入依赖
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml配置
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
# 集群版
# defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
instance:
instance-id: payment8001
#访问路径可以显示IP地址
prefer-ip-address: true
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
#lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
#lease-expiration-duration-in-seconds: 2
启动类配置
@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8001
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
搭建Eureka 服务消费方
引入依赖
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml配置
server:
port: 80
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
# 集群版
# defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
启动类
package com.pyy.springcloud;
@EnableEurekaClient
@SpringBootApplication
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = SelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
配置负载均衡
SelfRule.class
为自己配置的负载均衡策略,默认不配为轮询
package com.pyy.myrule;
@Configuration
public class SelfRule {
@Bean
public IRule iRule() {
return new RoundRobinRule();
}
}
该类必须与启动类包的同级包下面才会生效。
通过配置RestTemplate进行远程调用服务,集群模式通过Eureka Provider的名称进行远程调用时,需要配置负载均衡。
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //配置负责均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
调用方式
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
RestTemplate restTemplate;
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult<>(444,"操作失败");
}
}
自定义自己的负载均衡方法
编写负载均衡接口,ServiceInstance
获取的服务实例。
public interface LoadBalance {
ServiceInstance instance(List<ServiceInstance> instanceList);
}
实现类
@Component
public class Mlb implements LoadBalance {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current, next));
System.out.println("*****第" + next + "次访问,次数" + next);
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> instanceList) {
int index = getAndIncrement() % instanceList.size();
return instanceList.get(index);
}
}
调用方式
@RestController
public class OrderController {
@Resource
RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@Resource
private LoadBalance loadBalancer;
@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.instance(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
}
注意:使用自定义的负载均衡方法时,需要把定义RestTemplate时加的@LoadBalanced
注解去掉。
配置Eureka其他信息
关闭Eureka自我保护模式。默认情况下自我保护机制是打开的,线上建议都是打开的,即不需要设置。如果在测试环境需要设置为关闭,可以通过如下配置
eureka:
server:
enable-self-preservation: true
上报服务真实健康状态
SpringBoot的健康监测
<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>
自定义配置健康状态
@Service
public class HealthStatusService implements HealthIndicator {
private Boolean status = true;
public void setStatus(Boolean status) {
this.status = status;
}
@Override
public Health health() {
if (status) {
return new Health.Builder().up().build();
}
return new Health.Builder().down().build();
}
public String getStatus() {
return this.status.toString();
}
配置安全设置
直接引入security的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置security的账户密码
spring:
security:
user:
name: admin
password: 123456
同时服务注册需要修改配置为
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://admin:123456@localhost:7001/eureka
# 集群版
# defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
Zookeeper
CAP定理: Zookeeper是CP (一致性,分区容错性)
服务端
只需下载zookeeper的服务端压缩包,解压后运行即可。
服务提供方
引入依赖
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的zookeeper-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.8版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
根据服务端的zookeeper版本配置版本号
yml配置
server:
port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
启动类配置
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
Consul
Nacos
二、负载均衡
Ribbon
基于服务端的负载均衡
Ribbon 默认的策略是轮询
超时配置
# 请求连接的超时时间
ribbon.ConnectTimeout=2000
# 请求处理的超时时间
ribbon.ReadTimeout=5000
也可以为每个Ribbon客户端设置不同的超时时间, 通过服务名称进行指定:
ribbon-config-demo.ribbon.ConnectTimeout=2000
ribbon-config-demo.ribbon.ReadTimeout=5000
当我们禁用了 Eureka 之后,就不能使用服务名称去调用接口了,必须指定服务地址。
# 禁用 Eureka
ribbon.eureka.enabled=false
三、服务调用
Openfeign
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
配置Api接口
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //不结合Eureka时,写具体的uri地址
public interface PaymentFeignService {
@PostMapping(value = "/payment/create")
CommonResult create(@RequestBody Payment payment);
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
String paymentFeignTimeout();
}
配置feign日志监控级别
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
}
再yml中指定该类
logging:
level:
# feign日志以什么级别监控哪个接口
com.pyy.springcloud.service.PaymentFeignService: debug
调用方式
@RestController
public class OrderFeignController {
@Resource
PaymentFeignService paymentFeignService;
@GetMapping("/consumer/payment/create")
public CommonResult create(Payment payment) {
return paymentFeignService.create(payment);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPayment(@PathVariable("id") Long id) {
return paymentFeignService.getPaymentById(id);
}
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout(){
return paymentFeignService.paymentFeignTimeout();
}
}
启动类开启Openfeign的自动注入
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}