注册中心
Eureka
导入依赖
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
配置文件
- 注册中心默认端口:8761
server:
port: 8761 # 默认端口
eureka:
instance:
hostname: localhost # Eureka服务端实例名称
client:
registerWithEureka: false # 是否向Eureka注册自己(本身就是注册中心)
fetchRegistry: false # false 表示自己是注册中心
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 注册中心监控页面地址
spring:
application:
name: eureka-server
启动类
- @EnableEurekaServer:表示为一个Eureka服务启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaServerApplication.class).run(args);
}
}
服务注册
导入依赖
<dependencies>
<!-- eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 完善监控信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 表示为一个web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
</dependencies>
配置文件
- instance-id:实例化id与其他服务重名时,注册中心只能找到最后一次注册
server:
port: 8001
spring:
application:
name: product-data-service
datasource:
username: jerel
password: 4464984
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
# 服务注册到哪里
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/ # 注册中心的地址
instance:
instance-id: spirngcloud-provider-user # 实例化id
# info信息(注册中心可以获取到)
info:
app.name: user-service
company.name: top.xiongyungang
- 服务状态
- 服务信息
启动类 - @EnableEurekaClient:在服务启动后自动注册到Eureka
@SpringBootApplication
@EnableEurekaClient
public class UserServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(UserServerApplication.class).run(args);
}
}
服务保护机制
即服务宕机不会立刻在注册中心注销该服务,可在配置中关闭这种机制
服务调用
负载均衡(LB)
集中式负载均衡
在服务的消费方和提供方之间使用独立的负载均衡设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责吧访问请求通过某中策略转发至服务的提供方
进程式负载均衡
将负载均衡逻辑集成到消费方,消费方从服务注册中心获知那些地址可用,然后自己再从这些地址中选择一个合适的服务器,例如ribbon,ribbon只是一个类库,集成在消费方进程,消费方通过它来获取到服务提供方的地址
Ribbon
- Ribbon是Netflix下的开源项目,实现了客户端负载均衡
- 可自定义负载均衡算法
导入依赖
- netflix依赖中包含Ribbon
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置
server:
port: 8004
spring:
application:
name: ribbon-view-service
# Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # 注册中心地址,多个注册中心逗号分隔
instance:
instance-id: springcloud--ribbon-view # 实例id
配置类
- Ribbon默认的负载均衡算法为RandomRule(轮询方式)
@Configuration
public class ConfigBean {
// 配置负载均衡实现restTemplate
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
// 自定义负载均衡算法
@Bean
public IRule myRule() {
return new MyRandomRule();
// return new RandomRule(); //默认轮询方式
}
}
自定义负载均衡
- 自定义负载均衡,实现IRole接口或继承实现了IRole接口类
- 自定义类放在子目录中,防止被spring扫描到
// 实现IRule
// RandomRule: 随机策略 在while循环内,如果服务地址不为空会不停的循环直到随机出一个可用的服务
// RoundRobinRule: 轮询策略,但是有个查找次数的限制,也就是说查了10次都是不可用的服务的话就会警告没有可用服务并返回null了,选择的方式是很简单,取余运算
// RetryRule: 采用了轮询策略(内部直接实例化RoundRobinRule使用)的重试策略来获取可用的服务实例
// ...
public class MyRandomRule extends AbstractLoadBalancerRule {
private int cnt = 0; // 服务执行次数
private int currIndex = 0; // 当前服务索引
public MyRandomRule() {
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers(); // 可用服务列表
List<Server> allList = lb.getAllServers(); // 全部服务列表
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// 每个服务访问两次
//////////////////////////
if (cnt < 2) {
cnt++;
server = upList.get(currIndex);
} else {
cnt = 1;
currIndex++;
if (currIndex >= upList.size()) {
currIndex = 0;
}
server = upList.get(currIndex);
}
////////////////////////////
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
启动类
- @RibbonClient:启动时加载自定义Ribbon客户端
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "PRODUCT-DATA-SERVICE", configuration = MyRule.class) //启动时加载自定义Ribbon客户端
@Controller
public class RibbonViewApplication {
@Autowired
RestTemplate restTemplate; // 提供多种远程调用http服务的方式,简单的restful模板
@RequestMapping("/users")
@ResponseBody
public List<User> listUser() {
// 通过服务名称来访问服务
return restTemplate.getForObject("http://PRODUCT-DATA-SERVICE/users", List.class);
}
public static void main(String[] args) {
SpringApplication.run(RibbonViewApplication.class, args);
}
}
Feign
与Ribbon的不同
- Feign基于注解和接口
- 集成了Ribbon
导入依赖
<dependencies>
<!-- eureka 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- feign方式 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 表示web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置
server:
port: 8003
spring:
application:
name: view-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
配置Feign
- @FeignClient:使用服务名称获取服务
// Feign客户端,value:调用服务的注册名称
@FeignClient(value = "PRODUCt-DATA-SERVICE")
@Component
public interface ViewClient {
@GetMapping("/users")
public List<User> list();
}
启动类
- @EnableFeignClients:标记为Feign方式
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // Feign方式
@Controller
public class ViewServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ViewServiceApplication.class).run(args);
}
@Autowired
ViewClient viewClient;
@RequestMapping("/users")
@ResponseBody
public Object listUser() {
return viewClient.list();
}
}
路由网关Zuul
功能:提供 代理+路由+过滤功能
导入依赖
<dependencies>
<!-- Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
配置
server:
port: 9527
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # 注册中心地址
instance:
instance-id: zuul # 实例化id
prefer-ip-address: true # 隐藏真实ip地址
zuul:
routes:
myservice1: # 自定义
serviceId: PRODUCT-DATA-SERVICE #服务实例id,大写
path: /data/** # 路由地址
myservice2:
serviceId: VIEW-SERVICE
path: /view/**
ignored-services: "*" # 隐藏真实项目路径,*代表全部
prefix: /roles #设置公共路由前缀
启动类
- @EnableZuulProxy:标记为Zuul路由
@SpringBootApplication
@EnableZuulProxy //Zuul路由
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
服务熔断Hystrix
依赖
<dependencies>
<!-- Hystrix熔断服务支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- eureka 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- feign方式 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 表示web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置
server:
port: 8003
spring:
application:
name: view-service
feign:
hystrix:
enabled: true # 开启服务熔断
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # 注册中心地址
instance:
instance-id: springcloud-hystrix-view # 实例化id
prefer-ip-address: true # 隐藏真实ip地址
对类的熔断
模拟调用服务宕机
- @FeignClient注解的fallback指定一个实现当前接口的类,在服务调用失败时,调用该类的方法实现
// Feign客户端,value:调用服务的注册名称, fallback 熔断类
@FeignClient(value = "PRODUCt-DATA-SERVICE", fallback = ViewHystrixClient.class)
@Component
public interface ViewClient {
@GetMapping("/users")
Object list();
}
熔断类
- 实现了ViewClient接口,服务调用失败时调用该类方法实现
// 服务熔断后的备选方案
@Component
public class ViewHystrixClient implements ViewClient {
public Object list(){
return "server error";
}
}
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // Feign方式
@Controller
public class ViewHystrixServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ViewHystrixServiceApplication.class).run(args);
}
@Autowired
ViewClient viewClient;
@RequestMapping("/users")
@ResponseBody
public Object listUser() {
return viewClient.list();
}
}
对方法的熔断
启动类
- @EnableCircuitBreaker :对熔断的支持
- @HystrixCommand(fallbackMethod = "hystrix"):当前方法执行异常时跳转到指定方法实现
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // Feign方式
@Controller
@EnableCircuitBreaker // 对熔断的支持
public class ViewHystrixServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ViewHystrixServiceApplication.class).run(args);
}
@RequestMapping("/getUser")
@HystrixCommand(fallbackMethod = "hystrix")
@ResponseBody
public Object getuser() {
int id = 11;
if (id > 10) {
// 模拟故障
throw new RuntimeException("server error");
}
// 使用模拟数据
return new User(id, "testUser", "", "", "", "");
}
public Object hystrix() {
return "server error";
}
}