@SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式:
学习内容:1.服务拆分-服务远程调用
2.搭建eureka服务
-2.1.eureka服务注册-client
-2.2.eureka服务发现-服务拉取
3.Ribbon负载均衡
-3.1.饥饿加载
4.基于Feign远程调用
1.服务拆分-服务远程调用: 微服务根据业务模块拆分,做到单一职责,这时一个业务模块对应一个数据库,当我订单需要查询是对应哪个用户,就需要跨库查询
先确立思路,在order模块中的pojo实体类封装了userId和user,可以通过userId到数据库中查询到该user的数据并封装,最后返回该订单信息
实现步骤:
第一步:创建RestTemplate并注入Spring容器
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
代码位置:写在order模块的主类中
第二步:在order的service层中,找到查询的方法,利用restTemplate的getForObject(url,User.class)获得User
getForObject:
参数一:传递地址,查询的请求,查询到该用户
参数二:传递返回值对象的字节码
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发送http请求,查询用户
// 2.1.url路径
String url = "http://localhost:8081/user/" + order.getUserId();
// 2.2.发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
两步轻松搞定
重点在于restTemplate的getForObject方法
2.搭建eureka服务: 以上只是一个基础,明白一个概念,如果user服务增多,在restTemplate.getForObject的url中写死地址就不管用了
这时候就需要引入eureka,将微服务的信息注册进来,登记在eureka,eureka本身也是一个微服务也就会将自己注册进来。
每三十秒会发送一次注册信息,检查服务是否挂了。
搭建EurekaServer一共三步
第一步:创建新模块EurekaServer,引入eureka-server依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-server
第二步:添加@EnableEurekaServer 注解
@EnableEurekaServer // 自动装配
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
第三步:在eureka的application.yml中配置eureka地址
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
# eureka本身也是微服务,也会将自己注册到微服务上
eureka:
client:
service-url:
# eureka的地址信息,有多个用逗号隔开,将来可能有多个eureka,组成eureka集群
defaultZone: http://127.0.0.1:10086/eureka
2.1.eureka服务注册-client
在我们的项目结构中,eureka是服务端,导入的依赖就是server,那么有服务端必然有客户端,其他的微服务就是一个个的客户端,所以导入的依赖是client,注意这个区别。
实现步骤,一共两步,和服务端相比只是少加一个注释:
第一步:在user-service的pom.xml导入客户端的依赖
org.springframework.cloud spring-cloud-starter-netflix-eureka-client
第二步:在user-service微服务的application.yml中配置user-service的地址,第二步和前面配置eureka写的内容区别只是在于修改服务名称
#这边可能有同学复制粘贴后发现爆红,原因是yml中不能出现两段spring,合并他们就好了
spring:
application:
name: userservice # userservice的服务名称
# 注册服务到eureka上
eureka:
client:
service-url:
# eureka的地址信息,这边写的是将信息注册到哪?注册到 eureka 上
defaultZone: http://127.0.0.1:10086/eureka
实现结果:
这三服务我都写完后,重启这三个服务,访问eureka可以看到注册进来的服务。
过了三十秒后,我重新访问eureka,可以看到只有三条服务了。
模拟启动两个user-service:
第一步,选中服务复制配置
第二步:写上-Dserver.port=8082,注意避开端口冲突
第三步:多出了一个服务,启动它
第四步:重新访问eureka,可以看到user-service有两个端口了
小结:从大观上看,可以想象成一共就是两个微服务,eureka作为服务端,其他微服务都作为客户端,将自己的信息注册到eureka服务端上。同时,eureka本身也是一个微服务,所以也需要将自己的信息登记到eureka上。
实际操作的时候,eureka搭建完后,只需要重复2.1.小节的两个步骤。
能够理解并操作起来,恭喜你已经学会服务注册了!
第一步:修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口,不再写死代码。
// 2.1.url路径 String url = "http://userservice/user/" + order.getUserId();
原:参考第一节
// 2.1.url路径 String url = "http://localhost:8081/user/" + order.getUserId();
防止同学们看不懂或者忘记了,我贴出完整代码吧。注意是order-service的service层,是order去拉取user,userservice对应的是user-service的yml文件中spring.application.name服务名称
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发送http请求,查询用户
// 2.1.url路径
String url = "http://userservice/user/" + order.getUserId();
// 2.2.发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
第二步:在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:@LoadBalanced
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
学会后简直不要太爽!访问了两次不同的id,两个user-service都有查询,实现了负载均衡。
战术小结:
1.修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口。
2.在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:@LoadBalanced
不再写死地址,简单一句话:通过服务名访问目标服务。写上一个负载均衡的注解,即可实现负载均衡,如果有同学想专研什么是负载均衡,可以自行查找资料,本章只说简单明了的使用。
一共就两个步骤,轻松掌握!
第二节总结:
1.搭建EurekaServer
- 引入eureka-server依赖
- 添加@EnableEurekaServer // 自动装配
- 在EurekaServer的application.yml中配置eureka地址
2.服务注册
- 引入eureka-client依赖
- 在其他微服务的application.yml中配置eureka地址
3.服务发现
- 引入eureka-client依赖
- 在其他微服务的application.yml中配置eureka地址(实际重复2,给每个微服务配置eureka地址)
- 给RestTemplate添加@LoadBalancesd注解
- 用服务提供者的服务名称远程调用(服务名称就是application.yml配置中的spring.application.name服务名称)再给大伙展示一次,加深记忆
spring:
# 实际中大家肯定也有这些数据库的连接配置
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
#今天重点在这
application:
name: orderservice # orderservice的服务名称
3.Ribbon负载均衡
负载均衡前面的理论大家可以去黑马程序员的视频中了解吧,实际上也不会用到随机分配的形式,这里我稍微记录一下修改负载均衡规则的两种方式:
1.代码方式:在order-service中的OrderApplication类中,定义一个新的IRule
这种方式的服务范围是全体。
@Bean
public IRule randomRule() {
return new RandomRule();
}
这里图片中停用,只是因为两种方式二选一,我选择了配置的方式,使用并没有任何问题。图片只是给大伙看一个结构和写的地方。
2.配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则。
这种方式只针对某个服务而言
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
# 配置负载均衡规则,写上全限定类名
3.1饥饿加载
Ribbon默认采用的是懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
# 还是在Order-service,只是举例,比如订单要拉取用户信息,所以修改订单服务的配置
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: # 指定饥饿加载的服务名称,clients是一个集合,可以指定多个服务
- userservice
# - xxxservice
# - ...
4.基于Feign远程调用
http客户端Feign
使用Feign的步骤如下三步:(依旧以order和user举例)
第一步:引入依赖。
找到order的pom.xml文件
org.springframework.cloud spring-cloud-starter-openfeign
第二步:在order-service的启动类添加注解@EnableFeignClients开启Feign的功能。
@SpringBootApplication
//添加这个注解,与前面的代码变化不大,复制注解即可,其他的无需多余复制
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
第三步:声明一个远程调用,编写Feign客户端:
主要是基于SpirngMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
创建客户端,做接口声明
并添加@FeignClient注解,声明时user的微服务名称,并写上返回值的方法
//路径
@FeignClient("userservice")
public interface UserClient {
//返回值
@GetMapping("/user/{id}")
User findUserById (@PathVariable("id") Long id);
}
对照以前的代码
//路径 String url = "http://userservice/user/" + order.getUserId(); //返回值 User user = restTemplate.getForObject(url, User.class);
一一对应
接下来改造order的service层以前的代码
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用Feign远程调用
User user = userClient.findUserById(order.getUserId());
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
对比
原先的代码
修改后
好处:不再代码中写url,url后面的参数可能会有很多,在代码中实现会有一长串的代码,把原来代码中的url放到接口中管理。
而且Feign集成了负载均衡的功能
战术小结:
Feign的使用步骤
- 引入依赖
- 在主类上添加@EnableFeignClients注解
- 编写FeignClient接口
- 使用FeignClient中定义的方法代替RestTemplate
PS:以上讲解都是对消费者的操作,
比如user为order提供数据,就是提供者,
order拉取user的数据,就是消费者。
# 该学习还未结束,仍在更新中:



