@Configuration
public class ApplicationContextConfig {
// 配置bean 不然后面没法依赖注入,就像以前ssm整合时配置依赖注入一样,
// 需要在配置文件配置之后,代码中才可以依赖注入
// 当前文件就是spring的配置文件
@Bean
// @LoadBalanced //让这个RestTemplate在请求时拥有客户端负载均衡的能力 //将此注解注释掉,使用自己的轮询算法不使用默认的
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
@RestController
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/testRPC/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") String id) {
return restTemplate.getForObject(PAYMENT_URL + "/test/" + id, CommonResult.class);
}
}
前面我们没有服务注册中心,也可以服务间调用,为什么还要服务注册?
当服务很多时,单靠代码手动管理是很麻烦的,需要一个公共组件,统一管理多服务,包括服务是否正常运行,等
Eureka用于**服务注册,目前官网已经停止更新**
创建项目cloud-eureka-server-7001
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
dependencies>
yml配置文件
server:
port: 7001
# 单机版
eureka:
instance:
hostname: localhost #eureka服务端的实例名字
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}
启动类
/**
* @author WSKH
* @date 2020/12/19 14:12
* @description 注册中心
*/
@SpringBootApplication
@EnableEurekaServer // 启动Eureka服务
public class CloudEurekaServer7001Application {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServer7001Application.class, args);
System.out.println("启动成功!");
}
}
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
yaml配置
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
register-with-eureka: true #是否向注册中心注册自己
fetchRegistry: true #是否从注册中心抓取已有的注册信息 默认true,集群必须设置为true
service-url:
# 设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://localhost:7001 #单机版
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示IP地址
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
# lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
# lease-expiration-duration-in-seconds: 2
启动类
@SpringBootApplication
@EnableEurekaClient // 指定成为Eureka客户端
@EnableDiscoveryClient // 开启服务发现
@Slf4j
public class CloudProviderPayment8001Application {
public static void main(String[] args) {
SpringApplication.run(CloudProviderPayment8001Application.class, args);
log.info("cloud-provider-payment-8001 启动成功!");
}
}
单机版注册中心会出现单点故障,故一般都采用集群
两台或以上的Rureka互相注册,相互守望,对外暴露端口
7001的yaml配置
server:
port: 7001
#集群版
eureka:
instance:
hostname: eureka7001 #eureka服务端的实例名字(实际好像不起作用,DS Replicas是直接截取URL中冒号前面的部分,当作队友名,如http://localhost:7001,那就截取localhost当作队友名)
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
# defaultZone: http://eureka7001.com:7001
defaultZone: http://localhost:7002 #这个是集群版开启 互相注册
# server:
## 关闭自我保护机制,保证不可用服务被及时踢除
# enable-self-preservation: false
# eviction-interval-timer-in-ms: 2000
7002的yaml配置
server:
port: 7002
#集群版
eureka:
instance:
hostname: eureka7002 #eureka服务端的实例名字(实际好像不起作用,DS Replicas是直接截取URL中冒号前面的部分,当作队友名,如http://localhost:7001,那就截取localhost当作队友名)
client:
register-with-eureka: false #表识不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://localhost:7001
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true #是否向注册中心注册自己
fetchRegistry: true #是否从注册中心抓取已有的注册信息 默认true,集群必须设置为true
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://localhost:7001,http://localhost:7002 #集群版
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
register-with-eureka: true #是否向注册中心注册自己
fetchRegistry: true #是否从注册中心抓取已有的注册信息 默认true,集群必须设置为true
service-url:
# 设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://localhost:7001,http://localhost:7002 #集群版
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示IP地址
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
# lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
# lease-expiration-duration-in-seconds: 2
加入@LoadBalanced注解即可开启负载均衡
@Configuration
public class ApplicationContextConfig {
// 配置bean 不然后面没法依赖注入,就像以前ssm整合时配置依赖注入一样,
// 需要在配置文件配置之后,代码中才可以依赖注入
// 当前文件就是spring的配置文件
@Bean
@LoadBalanced //让这个RestTemplate在请求时拥有客户端负载均衡的能力 //将此注解注释掉,使用自己的轮询算法不使用默认的
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
测试
@RestController
public class OrderController {
// http://服务名大写
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/testLoadBalancedRpc/{id}")
public CommonResult<Payment> testLoadBalancedRpc(@PathVariable("id") String id) {
return restTemplate.getForObject(PAYMENT_URL + "/test/"+id, CommonResult.class);
}
}
最后发送请求 http://127.0.0.1:80/testLoadBalancedRpc/id=123 进行测试
发现会自动均衡地访问8001和8002
开启服务发现
使用DiscoveryClient完成服务发现
// 注入DiscoveryClient对象
@Resource
DiscoveryClient discoveryClient;
@GetMapping("/testDiscoveryClient")
public void testDiscoveryClient(){
// 从注册中心获取所有服务的名称
List<String> services = discoveryClient.getServices();
services.forEach(System.out::println);
// 根据服务名称找到其下的所有实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
// 遍历输出实例信息
instances.forEach(instance -> {
System.out.println(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
});
}
测试 http://127.0.0.1:8001/testDiscoveryClient
#集群版
eureka:
instance:
hostname: eureka7001 #eureka服务端的实例名字
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
# defaultZone: http://eureka7001.com:7001/eureka/
defaultZone: http://localhost:7002 #这个是集群版开启 互相注册
# server:
## 关闭自我保护机制,保证不可用服务被及时踢除
# enable-self-preservation: false
# eviction-interval-timer-in-ms: 2000
中文官网: https://www.spring.cloud.cc/spring-cloud-consul.html
英文官网:https://www.consul.io
需要下载一个安装包
查看版本
启动是一个命令行界面,需要输入consul agent -dev启动
http://localhost:8500
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
yaml配置
server:
port: 8006
spring:
application:
name: consul-provider-payment
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
启动类
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class CloudProviderconsulPayment8006Application {
public static void main(String[] args) {
SpringApplication.run(CloudProviderconsulPayment8006Application.class, args);
System.out.println("启动成功");
}
}
启动App,查看服务注册情况
默认我们使用eureka的新版本时,它默认集成了ribbon:
这个starter中集成了reibbon了
我们也可以手动引入ribbon
RestTemplate的:
xxxForObject()方法,返回的是响应体中的数据
xxxForEntity()方法.返回的是entity对象,这个对象不仅仅包含响应体数据,还包含响应体信息(状态码等)
IRule接口,Riboon使用该接口,根据特定算法从所有服务中,选择一个服务,
Rule接口有7个实现类,每个实现类代表一个负载均衡算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jqERrsdi-1686926847827)(…/…/AppData/Roaming/Typora/typora-user-images/image-20220423202944560.png)]
配置类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 此处将ribbon默认使用的轮询策略改为随机策略
return new RandomRule();
}
}
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
// 启动CLOUD-PAYMENT-SERVICE服务时去加载自定义的ribbon配置
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
@Slf4j
public class CloudConsumerOrder80Application {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerOrder80Application.class, args);
log.info("cloud-consumer-order-80 启动成功!");
}
}
public interface MyLoadBalancer {
/**
* 收集服务器总共有多少台能够提供服务的机器,并放到list里面
*
* @param serviceInstances
* @return ServiceInstance
* @author WSKH
* @date 2020/12/23 9:24
*/
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
@Component
public class MyLB implements MyLoadBalancer {
// 原子类
private final AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* @author WSKH
* @date 2020/12/23 10:07
* @description 判断时第几次访问
*/
public final int getAndIncrement() {
int current;
String a = "current";
int next = 0;
do {
current = atomicInteger.get();
// 防止越界
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!atomicInteger.compareAndSet(current, next));
System.out.println("*****第几次访问,次数next: " + next);
return next;
}
/**
* 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标, 每次服务重启动后rest接口计数从1开始。1
*
* @param serviceInstances
* @return ServiceInstance
* @author WSKH
* @date 2020/12/23 9:51
*/
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
// 注入自定义的负载均衡规则
@Resource
private MyLoadBalancer myLoadBalancer;
@Resource
private DiscoveryClient discoveryClient;
/**
* @author WSKH
* @date 2020/12/23 10:27
* @description 测试自定义的负载均衡规则
*/
@GetMapping(value = "/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.isEmpty()) {
return null;
}
// 调用自定义的负载均衡策略
ServiceInstance serviceInstance = myLoadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri + "/payment/lb", String.class);
}
未完待续...