# application.yml文件禁用自我保护模式
eureka:
server:
enable-self-preservation: false
每台eureka节点都注册到其他的eureka注册中心上,使得eureka节点之间可以互相拉取数据,保持数据同步。当一台eureka挂掉之后,客户端还可以继续使用其他的eureka,保证了eureka的高可用。
以三个eureka服务作集群为例:
C:\WINDOWS\system32\drivers\etc
该路径下的hosts文件配置如下:
# 文件尾部
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 activate.navicat.com
127.0.0.1 localhost1
127.0.0.1 localhost2
127.0.0.1 localhost3
spring:
application:
name: eureka
server:
port: 7001
servlet:
context-path: /
eureka:
instance:
# 给服务起别名
hostname: localhost1
server:
enable-self-preservation: false
client:
# 设置为 false,表示当前服务不要注册到注册中心。
registerWithEureka: false
# 设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost2:6001/eureka/, http://localhost3:5001/eureka/
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: eureka2
server:
port: 6001
servlet:
context-path: /
eureka:
instance:
hostname: localhost2
server:
enable-self-preservation: false
client:
# 设置为 false,表示当前服务不要注册到注册中心。
registerWithEureka: false
# 设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost1:7001/eureka/, http://localhost3:5001/eureka/
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: eureka3
server:
port: 5001
servlet:
context-path: /
eureka:
instance:
hostname: localhost3
server:
enable-self-preservation: false
client:
# 设置为 false,表示当前服务不要注册到注册中心。
registerWithEureka: false
# 设置为false,表示单节点的EurekaServer,不需要同步其他的EurekaServer节点的数据
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost2:6001/eureka/, http://localhost1:7001/eureka/
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: mocro-8001
server:
port: 8001
servlet:
context-path: /
eureka:
client:
serviceUrl:
defaultZone: http://localhost2:6001/eureka/
# , http://localhost1:7001/eureka/, http://localhost3:5001/eureka/
delete() 在特定的URL上对资源执行HTTP DELETE操作
exchange() 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的
execute() 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象
getForEntity() 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
getForObject() 发送一个HTTP GET请求,返回的请求体将映射为一个对象
postForEntity() POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的
postForObject() POST 数据到一个URL,返回根据响应体匹配形成的对象
headForHeaders() 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头
optionsForAllow() 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息
postForLocation() POST 数据到一个URL,返回新创建资源的URL
put() PUT 资源到特定的URL
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@SpringCloudApplication
@EnableFeignClients
public class Provider1Application {
public static void main(String[] args) {
SpringApplication.run(Provider1Application.class, args);
}
}
@FeignClient("provider1") //指定被调用端的微服务名
public interface Provider1Feign {
//直接把被调用端的方法名、返回值、参数、映射url粘过来就行,@ResponseBody可以不粘
@RequestMapping("proInfo")
public Map<String, Object> proInfo();
}
@Autowired
Provider1Feign provider1Feign;
@RequestMapping("recInfo1")
@ResponseBody
public Map<String, Object> recInfo1(){
Map<String, Object> map = provider1Feign.proInfo();
return map;
}
依赖引入
ribbon 基本上不需要单独引用,因为绝大多数的注册中心(eureka,consul等)都已集成了ribbon。
Ribbon作为一个负载均衡组件, 里面有一个个的负载均衡策略, 而这些策略的公共接口是IRule。
通过往Spring容器中注入一个IRule接口的实现类, 可以改变Ribbon默认的负载均衡策略(默认是轮询)。
ribbon相关配置
application.yml中相关配置
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间
# 被调用的微服务名
mocro-8001:
ribbon:
#指定使用Nacos提供的负载均衡策略
NFLoadBalancerRuleClassName: com.hand.config.balance.IPFirstLB
自定义一个配置类:
@Configuration
public class RibbonConfig {
@Bean
public IRule iRule(){
//可以自定义
return new RandomRule();//随机策略
}
}
添加@RibbonClient注解,指定配置类和要调用的微服务名
// name为微服务名称,必须和服务提供者的微服务名称一致,用来指定哪个服务使用该策略
// configuration为自定义的配置类,用于配置自定义的负载均衡规则
@RibbonClient(name = "mocro-8001",configuration = RibbonConfig.class)
@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {RibbonConfig.class})})
public class Micro9001Application {
public static void main(String[] args) {
SpringApplication.run(Micro9001Application.class, args);
}
@Bean //配置在此,或在启动类扫描得到的地方,则全局生效
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
// @Bean //配置在此,或在启动类扫描得到的地方,则全局生效
// public IRule iRule(){
// return new RandomRule();//随机策略
// }
}
// 优先把请求分发到ip相同的服务
public class IPFirstLB extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
// 读取配置文件, 并且初始化, ribbon内部基本上用不上
}
@Override
public Server choose(Object key) {
ILoadBalancer loadBalancer = this.getLoadBalancer();
//获取当前请求的服务的实例
List<Server> reachableServers = loadBalancer.getReachableServers();
List<Server> ipEqualServers = new ArrayList<>();
for(Server s : reachableServers){
if(s instanceof DiscoveryEnabledServer){
String serverIP = ((DiscoveryEnabledServer) s).getInstanceInfo().getIPAddr();
try {
String clientIP = InetAddress.getLocalHost().getHostAddress();
if(serverIP.equals(clientIP)){
ipEqualServers.add(s);
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
//没有ip相同的,则对全部用随机策略
if(ipEqualServers.isEmpty()){
int index = ThreadLocalRandom.current().nextInt(reachableServers.size());
return reachableServers.get(index);
}else { //有ip相同的,则对相同的这些用随机策略
int index = ThreadLocalRandom.current().nextInt(ipEqualServers.size());
return ipEqualServers.get(index);
}
}
}
ribbon.eager-load.enabled=true # 开启Ribbon的饥饿加载模式
ribbon.eager-load.clients=hello-service, user-service # 指定需要饥饿加载的微服务名
CAP定理
负载均衡与高可用
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
@SpringBootApplication
@EnableCircuitBreaker // 开启熔断器
public class Micro9001Application {
public static void main(String[] args) {
SpringApplication.run(Micro9001Application.class, args);
}
}
@RequestMapping("hytest")
@HystrixCommand(
// 每一个属性都是一个HystrixProperties
commandProperties = {
// 调用超时2秒则熔断
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
}
// 服务降级
,fallbackMethod = "fallbackMethod"
)
@ResponseBody
public Map<String, Object> hystrixTest(){
Map<String, Object> map = new HashMap<>();
map.put("msg", "service端口:" + port);
map.put("status", true);
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return map;
}
//指定一个降级的方法
//该方法返回值、参数要和接口方法相似
public Map<String, Object> fallbackMethod(){
Map<String, Object> map = new HashMap<>();
map.put("msg", "fallbackMethod 降级啦,service端口:" + port);
map.put("status", false);
return map;
}
java.util.concurrent.TimeoutException: null
at com.netflix.hystrix.AbstractCommand.handleTimeoutViaFallback(AbstractCommand.java:997) ~[hystrix-core-1.5.18.jar:1.5.18]
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
spring:
application:
name: receiver1
cloud:
nacos:
discovery:
server-addr: wayserkon.top:8848
sentinel:
transport:
dashboard: wayserkon.top:8858
management:
endpoints:
web:
exposure:
include: "*"
@Service
@Slf4j
public class SentinelTestServiceImpl implements SentinelTestService {
@Value("${server.port}")
private int port;
@Override
//value:资源名称,必需项(不能为空)
//blockHandler 对应处理 BlockException 的函数名称
//fallback 用于在抛出异常的时候提供 fallback 处理逻辑
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public Map<String, Object> sentinelTest() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "provider端口:" + port);
map.put("status", true);
return map;
}
// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback() {
log.error("helloFallback:{}",port);
return String.format("Halooooo %d", port);
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(BlockException ex) {
// Do some log here.
log.error("exceptionHandler:{}",port);
ex.printStackTrace();
return "Oops, error occurred at " + port;
}
}