Eureka组件
Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka Server本身也是一个服务,默认情况下会自动注册到Eureka注册中心。
如果搭建单机版的Eureka Server注册中心,则需要配置取消Eureka Server的自动注册逻辑。
eureka:
client:
# false不向注册中心注册自己
register-with-eureka: false
# false表示自己就是注册中心,自身的职责就是维护服务,不需要去检索服务
fetch-registry: false
Eureka Client
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Client分为两个角色:Service Provider和Service Consumer
Eureka结构原理
Register(服务注册):把自己的IP和端口注册给Eureka。
Renew(服务续约):发送心跳包,每30秒发送一次,告诉Eureka自己还活着。
Cancel(服务下线):当provider关闭时会向Eureka发送消息,把自己从服务列表中删除,防止consumer调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):eureka集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。
Eureka Server单机版搭建
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
# false不向注册中心注册自己
register-with-eureka: false
# false表示自己就是注册中心,自身的职责就是维护服务,不需要去检索服务
fetch-registry: false
service-url:
# 在C:\Windows\System32\drivers\etc\hosts 中配置127.0.0.1 eureka7001.com可以将localhost改为eureka7001.com
defaultZone: http://eureka7001.com:7001/eureka/
启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka Server集群版搭建
eureka集群与zookeeper集群的区别:
ZooKeeper注册中心集群搭建后,集群中各节点呈现主从关系,集群中只有主节点对外提供服务的注册和发现功能,从节点相当于备份节点,只有主节点宕机时,从节点会选举出一个新的主节点,继续提供服务的注册和发现功能。
而Eureka Server注册中心集群中每个节点都是平等的,集群中的所有节点同时对外提供服务的发现和注册等功能。同时集群中每个Eureka Server节点又是一个微服务,也就是说,每个节点都可以在集群中的其他节点上注册当前服务。又因为每个节点都是注册中心,所以节点之间又可以相互注册当前节点中已注册的服务,并发现其他节点中已注册的服务。
# Eureka Server1
server:
port: 7001
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
# Eureka Server2
server:
port: 7002
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 如果关联多个eureka server,链接中间逗号隔开,如
# http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
defaultZone: http://eureka7001.com:7001/eureka/
Eureka安全认证
<!-- spring boot security安全认证启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
# 使用http basic安全认证语法,在集群通信中增加认证信息。 http://用户名:密码@地址:端口/eureka/
eureka:
client:
serviceUrl:
defaultZone: http://user:pwd@eureka7001.com:7001/eureka
# 开启基于http basic的安全认证
security:
basic:
enabled: true
# 设置安全认证用户名
security:
user:
name: user
# 设置安全认证密码
security:
user:
password: pwd
Eureka Client
Application Service服务提供者
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
server:
port: 8001
spring:
application:
# 微服务名称,服务消费者调用服务提供者时通过微服务名称调用,ip:port改为微服务名称
private static final String URL = "http://CLOUD-PAYMENT-SERVICE/payment";
name: cloud-payment-service
eureka:
client:
# true表示向注册中心注册自己
register-with-eureka: true
# true表示需要去检索服务
fetch-registry: true
service-url:
# 多个Eureka Server需要配置多个,中间逗号隔开
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
启动类‘
@EnableEurekaClient
@SpringBootApplication
public class EurekaApplicationServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplicationServiceApplication.class, args);
}
}
Application Client服务消费者
启动类、yml文件与pom.xml与服务提供者相同
/**
* @author sangshanchun
* @brief
* @date 2020/11/18 20:12
*/
@RestController
@RequestMapping("/consumer")
public class OrderController {
private static final String URL = "http://CLOUD-PAYMENT-SERVICE/";
@Autowired
private RestTemplate restTemplate;
@ResponseBody
@GetMapping("/payment/create")
public CommonResult<User> create(User user) {
return restTemplate.postForObject(URL + "payment/create", user, CommonResult.class);
}
@ResponseBody
@GetMapping("/payment/get/{uid}")
public CommonResult<User> get(@PathVariable("uid") Integer uid) {
return restTemplate.getForObject(URL + "payment/get/" + uid, CommonResult.class);
}
}
/**
* 将RestTemplate注册到IOC容器中
*/
@Configuration
public class RestTemplateConfig {
@Bean
// 开启负载均衡
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
/**
* 轮询规则,配置为随机轮询
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule();
}
}
服务保护
什么是服务保护模式
服务保护模式:一般情况下,微服务在Eureka上注册后,会每30秒发送心跳包,Eureka通过心跳来判断服务时候健康,同时会定期删除超过90秒没有发送心跳服务。导致Eureka Server接收不到心跳包的原因:一是微服务自身的原因,二是微服务与Eureka之间的网络故障。
常微服务的自身的故障只会导致个别服务出现故障,一般不会出现大面积故障,而网络故障通常会导致Eureka Server在短时间内无法收到大批心跳。虑到这个区别,Eureka设置了一个阀值,当判断挂掉的服务的数量超过阀值时,Eureka Server认为很大程度上出现了网络故障,将不再删除心跳过期的服务。Eureka Server在运行期间,会统计心跳失败的比例在15分钟内是否低于85%,如果低于85%,Eureka Server则任务是网络故障,不会删除心跳过期服务。
这种不删除的,90秒没有心跳的服务,称为无效服务,但是还是保存在服务列表中。如果Consumer到注册中心发现服务,则Eureka Server会将所有好的数据(有效服务数据)和坏的数据(无效服务数据)都返回给Consumer。
关闭服务保护模式
eureka:
server:
enableSelfPreservation: false
优雅停服
Eureka Client会发送一个shutdown请求到Eureka Server,Eureka Server接收到这个shutdown请求后,会在服务列表中标记这个服务的状态为down,同时Eureka Client应用自动关闭。
依赖,服务提供者或服务调用者
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
默认不开启优雅停服,开启需要在yml文件中配置
# 启用shutdown,优雅停服功能
endpoints:
shutdown:
enabled: true
# 禁用密码验证
endpoints:
shutdown:
sensitive: false
通过post方式发送http请求:http://ip:port/shutdown 即可从Eureka Server中关闭服务提供者或服务调用者
OpenFeign
服务消费者通过OpenFeign调用服务提供者提供的接口
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类
@SpringBootApplication
@EnableFeignClients
public class ApplicationFeignOrder80 {
public static void main(String[] args) {
SpringApplication.run(ApplicationFeignOrder80.class, args);
}
}
服务消费者:controller
@RestController
@RequestMapping("/consumer")
public class FeignClientController {
@Autowired
private FeignClientService feignClientService;
@ResponseBody
@GetMapping("/payment/get/{uid}")
public CommonResult get(@PathVariable("uid") Integer uid) {
return feignClientService.getById(uid);
}
}
服务消费者:service
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface FeignClientService {
// 此接口方法与服务提供者提供的api相同,openfeign通过动态代理,
// 结合CLOUD-PAYMENT-SERVICE和/payment/get/{uid}访问服务提供者
@GetMapping("/payment/get/{uid}")
public CommonResult getById(@PathVariable("uid") Integer uid);
}
Hystrix熔断与降级
当某些功能出现异常、响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(兜底方法)错误处理信息,称为服务降级。
服务熔断类似于保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
开启断路器:circuitBreaker.enabled = true,比如设置某个方法的正常访问情况为:10秒内请求10次,通过率为50%及以上不熔断,低于50%熔断,熔断后断路器处于open状态,即使正常访问也会返回降级的方法返回值。当通过率高于50%后断路器关闭。
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallBack", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求10次
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//10秒
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")//10秒内通过率50%
})
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("id不能为负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "成功" + "\t" + serialNumber;
}
/**
* 兜底方法
*/
public String paymentCircuitBreakerFallBack(Integer id) {
return "id不能为负数,请稍后再试,id:" + id;
}
Feign的降级,启动类上需要加
@EnableFeignClients
@EnableHystrix
@EnableCircuitBreaker
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE", fallback = OrderFallBackService.class)
public interface OrderService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentOk(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentTimeOut(@PathVariable("id") Integer id);
}
@Component
public class OrderFallBackService implements OrderService{
@Override
public String paymentOk(Integer id) {
return "OrderService-FallBack-paymentOk";
}
@Override
public String paymentTimeOut(Integer id) {
return "OrderService-FallBack-paymentTimeOut";
}
}
GateWay
Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控、埋点和限流等。
Route:网关的基本构建模块,由一个id、目标url、一组断言和一组过滤器定义,如果断言为真,则路由匹配。
predicate:输入类型是一个ServerWebExchange,用于匹配来自HTTP请求的任何内容,如headers参数。实际上是实现一组匹配规则,方便让请求过来找到对应的路由进行处理。
filter:过滤器会对请求和响应进行修改处理
spring:
cloud:
gateway:
routes:
- id: time_route
uri: http://ityouknow.com
predicates:
# 通过时间匹配,有after、before、between三种
- After=2018-01-20T06:06:06+08:00[Asia/Shanghai]
# 通过host匹配
- Host=**.ityouknow.com
# 通过请求方式匹配
- Method=GET
# 通过请求路径匹配
- Path=/foo/**
# 通过请求参数匹配
- Query=smile
# 通过请求IP地址进行匹配
- RemoteAddr=192.168.1.1/24
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment-get #路由id不可为空
#uri: http://localhost:80 # 匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 通过服务名提供服务的路由地址,lb即load balance,负载均衡
predicates:
- Path=/payment/get/** #断言,路径相匹配的才进行路由
eureka:
instance:
hostname: cloud-gateway-service
prefer-ip-address: true
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ApplicationGateWay9527 {
public static void main(String[] args) {
SpringApplication.run(ApplicationGateWay9527.class, args);
}
}