why spring cloud?
- 跨语言–异构平台服务调用
- 可插拔–无状态,无强依赖,可随时下线,解决网络不可靠问题
- 牺牲一部分性能,提高可用性–RPC二进制比http json性能高很多
- RPC相当于QQ,HTTP相当于微信
Spring Cloud–生态
- 服务–Spring Boot
- 模板引擎–Thymeleaf
- 服务注册–Netflix Eureka
- 服务调用–Netflix Fegin
- 负载均衡–Netflix Ribbon
- 熔断降级–Netflix Hystrix
- 服务网管–
- Netflix Zuul–业务网关
- Spring Cloud Gateway–业务网关
- Nginx–流量网关
- WAF
- 定向流量分发
- 负载策略
- Kong
- Openresty (Nginx+Lua)
- 权限认证
- Spring Security–OAuth2.0
- Spring Shiro
- Spring Session
- JWT
- 分布式事务
- Alibaba Seata
- TCC
- 分布式锁
- Zookeeper Curator
- RedLock
- 链路追踪
- Spring Cloud Sleuth
- zipkin
- 健康检查–Spring Boot Actuator
- 监控平台–Spring Cloud Admin
- 消息总线–Spring Cloud Bus
- Spring Cloud Stream
- Spring Integration
- Spring Messaging
- Apache Camel
Eureka
自我保护机制
客户端每分钟续约数量小于客户端总数的85%时触发保护机制
- eureka.server.enable-self-preservation=false;
Server
- 注解:@EnableEurekaServer
- application.properties
#是否将自己注册到Eureka Server,默认为true
eureka.client.register-with-eureka=false
#是否从Eureka Server获取注册信息,由于单节点,不需要同步其他节点数据,用false
eureka.client.fetch-registry=false
#设置服务注册中心URL,用于client和server端交流
eureka.client.service-url.defaultZone=http://localhost:7900/eureka
#集群配置
#eureka.instance.appname=EurekaServer
#server.port=7200
#eureka.instance.hostname=euk2.com #用来查找主机地址的
#eureka.client.service-url.defaultZone=http://euk1.com:7100/eureka
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
Provider
- Register–服务注册,在第一次心跳的时候提交
- Renew–心跳,client向server端每30秒发起一次心跳;若90秒没有更新,则client端删除注册表实例
- Fetch Registry–client从server获取注册表信息并缓存到本地
- Cancel–client关闭时通知server,从server实例注册表中删除实例
- Time Lag–同步时间延迟。更改传播到所有client可能需要2分钟
- Communication mechanism–通讯机制。Http协议下的Rest请求,默认使用Jersey、Jackson、JSON完成节点通讯
注册
-
application.properties
spring.application.name=provider server.port=80 eureka.client.service-url.defaultZone=http://euk1.com:7100/eureka #自定义信息 #eureka.instance.metadata-map.dalao=huangkb--provider
-
pom.xml
<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>
服务列表查看
- https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
- http://euk1.com:7100/eureka/apps
- http://euk1.com:7100/eureka/status
- http://euk1.com:7100/eureka/apps/provider
Consumer
-
application.properties
spring.application.name=consumer server.port=90 eureka.client.service-url.defaultZone=http://euk1.com:7100/eureka
获取服务列表信息
EurekaClient 版本
@Qualifier("eurekaClient")
@Autowired
EurekaClient client2;
@GetMapping("/client4")
public Object client4() {
//获取单个
//List<InstanceInfo> instances = client2.getInstancesById("localhost:provider:80");
//获取所有
List<InstanceInfo> instances = client2.getInstancesByVipAddress("provider", false);
if (instances.size() > 0) {
InstanceInfo instanceInfo = instances.get(0);
if (instanceInfo.getStatus() == InstanceInfo.InstanceStatus.UP) {
String url = "http://" + instanceInfo.getHostName() + ":" + instanceInfo.getPort() + "/getHi";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr:" + respStr);
}
}
return "client4";
}
LoadBalancerClient 版本
-
lb 做了负载均衡,默认过滤掉了DOWN的状态
@Autowired LoadBalancerClient lb; @GetMapping("/client5") public Object client5() { //lb 做了负载均衡,默认过滤掉了DOWN的状态 ServiceInstance instance = lb.choose("provider"); //String url = "http://" + instanceInfo.getHostName() + ":" + instanceInfo.getPort() + "/getHi"; String url = instance.getUri() + "/getHi"; System.out.println(url); RestTemplate restTemplate = new RestTemplate(); String respStr = restTemplate.getForObject(url, String.class); System.out.println("respStr:" + respStr); return "client5"; }
服务上下线
只是改变了服务在Eureka Service中的状态,服务下线为DOWN
/**
* 手动操作服务上下线
*/
@Service
public class HealthStatusService implements HealthIndicator {
public boolean getStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
private Boolean status = true;
@Override
public Health health() {
if (status) {
return new Health.Builder().up().build();
}
return new Health.Builder().down().build();
}
}
actuator–健康上报
-
pom.xml
<!--节点信息上报--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
application.properties
#上报信息 management.endpoints.web.exposure.include=* #允许手动关闭tomcat management.endpoint.shutdown.enabled=true #上报真实健康状态 eureka.client.healthcheck.enabled=true
security–安全验证
-
pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-security</artifactId> </dependency>
-
application.properties
#安全认证 spring.security.user.name=huangkb spring.security.user.password=123456
-
关闭方式跨域攻击
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable();//关闭跨域攻击 super.configure(http); } }
-
远程连接
eureka.client.service-url.defaultZone=http://huangkb:123456@euk1.com:7100/eureka
Ribbon
负载均衡算法
负载均衡算法–自定义
public static AtomicInteger atomicInteger = new AtomicInteger();
@GetMapping("/client7")
public Object client7() {
List<ServiceInstance> instances = client.getInstances("provider");
int nextInt = 0;
//随机负载均衡
nextInt = new Random().nextInt(instances.size());
//轮训负载均衡
int i = atomicInteger.getAndIncrement();
nextInt = i % instances.size();
//权重负载均衡
//hash负载均衡
ServiceInstance instance = instances.get(nextInt);
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/getHi";
System.out.println(url);
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr:" + respStr);
return "client7";
}
负载均衡策略–自定义
//自定义负载均衡策略
@Bean
public IRule myRule() {
//return new RoundRobinRule();//默认
return new RandomRule();
//return new RetryRule();
}
ribbon负载均衡配置
-
application.properties
#负载均衡策略 provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
ribbion直连
#直连
ribbon.eureka.enabled=false
ribbon.listOfServers=localhost:80
RestTemplate–远程服务调用
基于LoadBalanced远程
@LoadBalanced
@Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
@GetMapping("/client9")
public Object client9() {
// ServiceInstance instance = lb.choose("provider");
// String url = instance.getUri() + "/getHi";
String url = "http://provider/getHi";
System.out.println(url);
String respStr = restTemplate.getForObject(url, String.class);
System.out.println("respStr:" + respStr);
return "client9";
}
拦截器
@LoadBalanced
@Bean
RestTemplate getRestTemplate() {
RestTemplate restTemplate=new RestTemplate();
restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());
return restTemplate;
}
//拦截器
public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
System.out.println("");
System.out.println("拦截器--拦截啦");
System.out.println("拦截器url:"+request.getURI());
ClientHttpResponse response = execution.execute(request, body);
System.out.println("拦截器res--"+response.getHeaders());
return response;
}
}
Feign–声明式服务调用
@EnableFeignClients----feign本质就是RestTemplate进一步封装
方式一
@FeignClient(name = "xxoo", url = "http://localhost:82")
public interface UserApi {
@GetMapping("/alive")
public String alive();
}
方式二
@FeignClient(name = "user-provider")
public interface UserApi {
@GetMapping("/alive")
public String alive();
}
方式三
面向接口编程,类似dubbo
-
UserApi 通用接口类包
@RequestMapping("/User") public interface UserApi { /** * hello 测试 * @return xxoo */ @GetMapping("/alive") public String alive(); }
-
consumer extends UserApi 调用
@FeignClient(name = "user-provider") public interface ConsumerApi extends UserApi { } @RestController public class MainController { @Autowired ConsumerApi api; @GetMapping("/alive") public String alive() { return api.alive(); } }
-
provider implement UserApi 实现方法
@RestController public class UserController implements UserApi { @Override public String alive() { return "xxoo"; } }
Hystrix–断路器
未完待续```