零、本文纲要
- 一、服务暴露&调用
- 二、服务注册&发现
- 三、服务注册&发现(集群)
- 四、负载均衡
- 五、负载均衡部分源码
tips:Ctrl + F快速定位到所需内容阅读吧。
一、服务暴露&调用
这里我们设置2个服务,一个订单服务OrderService,一个用户服务UserService。
- 0、数据库tb_order表和tb_user表创建
tb_order表
CREATE TABLE `tb_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`name` varchar(100) DEFAULT NULL COMMENT '商品名称',
`price` bigint(20) NOT NULL COMMENT '商品价格',
`num` int(10) DEFAULT '0' COMMENT '商品数量',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `username` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
tb_user表
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL COMMENT '收件人',
`address` varchar(255) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
- 1、pom.xml配置基础依赖
此处order服务和user服务的基础依赖一致
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
mysql
mysql-connector-java
runtime
org.springframework.cloud
spring-cloud-starter
org.projectlombok
lombok
true
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
- 2、编写实体类
order实体类//注意:Order信息内有User信息
@Data
public class Order {
private Long id;
private Long price;
private String name;
private Integer num;
private Long userId;
private User user; //注意:Order信息内有User信息
}
user实体类
@Data
public class User {
private Long id;
private String username;
private String address;
}
- 3、编写mapper接口
OrderMapper接口
public interface OrderMapper {
@Select("select * from tb_order where id = #{id}")
public Order findById(Long id);
}
UserMapper接口
public interface UserMapper {
@Select("select * from tb_user where id = #{id}")
public User findById(@Param("id") Long id);
}
- 4、编写Service接口&实现类
OrderService接口&实现类
注意:此处RestTemplate能够基于Rest风格进行远程服务调用,比如我们查询使用GET方法getForObject。
public interface OrderService {
public Order queryOrderById(Long id);
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Override
public Order queryOrderById(Long id){
Order order = orderMapper.findById(id);
String url = "http://localhost:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
}
UserService接口&实现类
public interface UserService {
public User queryUserById(Long id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserById(Long id){
return userMapper.findById(id);
}
}
- 5、Web层Controller类
OrderController类
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/{id}")
public Order queryOrderById(@PathVariable("id") Long id){
return orderService.queryOrderById(id);
}
}
UserController类
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/{id}")
public User queryUserById(@PathVariable("id") Long id){
return userService.queryUserById(id);
}
}
- 6、配置文件application.yml
order服务的配置
# server-port
server:
port: 8082
# datasource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
# mybatis
mybatis:
type-aliases-package: com.stone.pojo
configuration:
map-underscore-to-camel-case: true
# logging
logging:
level:
com.stone: debug
pattern:
dateformat: yyyy-MM-dd HH:mm:ss:SSS
user服务的配置
# server-port
server:
port: 8081 #此处user服务和order服务配置不同端口
# datasource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false #此处user服务和order服务配置不同数据库连接
# mybatis
mybatis:
type-aliases-package: com.stone.pojo
configuration:
map-underscore-to-camel-case: true
# logging
logging:
level:
com.stone: debug
pattern:
dateformat: yyyy-MM-dd HH:mm:ss:SSS
- 7、启动order和user服务,进行服务暴露&调用
user服务调用:http://localhost:8081/user/1
order服务调用:http://localhost:8082/order/101
在order服务内部我们使用restTemplate进行了user服务的调用,具体代码如下:
@Override
public Order queryOrderById(Long id){
Order order = orderMapper.findById(id);
String url = "http://localhost:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
这样,我们就实现了服务暴露&调用,但是此处仅是基本实现,如果user服务是集群的话就不够优雅。因为此处我们是硬编码的形式将服务url编写到服务内的。
二、服务注册&发现
- 1、Eureka注册中心
①pom.xml基础依赖
Eureka server的基础依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
②application.yml配置
# server port
server:
port: 8088
# application name
spring:
application:
name: eurekaserver
# eureka url
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8088/eureka
③@EnableEurekaServer开启Eureka容器
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaApplication.class, args);
}
}
访问:Eureka http://127.0.0.1:8088/
此时就可以看到,我们注册中心内部注册的服务实例了(eureka本身也会将自己注册为服务实例)。
- 2、order&user服务注册
①pom.xml基础依赖
Eureka client的基础依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
②application.yml配置
order服务相关配置:Ⅰ、应用名称;Ⅱ、服务中心地址。
spring:
application:
name: orderservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8088/eureka
user服务相关配置:Ⅰ、应用名称;Ⅱ、服务中心地址。
spring:
# application name
application:
name: userservice
# eureka server url
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8088/eureka
- 3、启动order、user服务,服务注册&发现
此时可以看到order服务和user服务已经注册到注册中心了,但是此时各项服务还是单例的,如果是集群我们还需做些调整。
三、服务注册&发现(集群)
- 1、IDEA模拟服务集群
Ⅰ 在想要部署集群的服务处右键-√Copy Configuration
Ⅱ 在Environment-VM options处设置对应的端口号:-Dserver.pot=8083
Ⅲ 正常启动服务即可
此时可以看到user服务已经是集群部署的。
- 2、服务消费方更新配置
Ⅰ RestTemplate类添加@LoadBalanced注解,以支持负载均衡。
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
Ⅱ 修改RestTemplate服务调用的代码。
@Override
public Order queryOrderById(Long id){
Order order = orderMapper.findById(id);
//String url = "http://localhost:8081/user/" + order.getUserId();
String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
此时,我们在Reload或者Restart消费方order服务,进行对应的查询时就是负载均衡的查询。
四、负载均衡
- 1、@LoadBalanced注解实现负载均衡
具体如上代码,此处不重复。 - 2、配置负载均衡策略类,实现负载均衡
Ⅰ 基础依赖
com.netflix.ribbon
ribbon-loadbalancer
2.3.0
Ⅱ 负载均衡实现类
注意:这种方式注册IRule实现类,会让整个order服务消费其他服务时采用此策略。
@Bean
public IRule randomRule(){
return new RandomRule();
}
Ⅲ 消费方application.yml配置,负载均衡规则
注意:在application.yml中单独配置userservice服务的负载均衡规则,这样可以更灵活配置多个服务不同负载均衡规则。
# user-service ribbon rule
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule# 负载均衡规则
Ⅳ 消费方application.yml中配置,指定服务饥饿加载
ribbon:
eager-load:
enabled: true
clients: userservice
五、负载均衡部分源码
- 1、LoadBalancerInterceptor#intercept方法
此处会得到:http://userservice/user/1
此处接着得到:userservice(服务名称)
- 2、BlockingLoadBalancerClient#execute方法
- 3、BlockingLoadBalancerClient#choose方法
此处我们就会得到对应的loadBalancer,也就是负载均衡策略;
以及我们的service实例serviceInstance。
RoundRobinLoadBalancer负载均衡的方法实现,感兴趣的还可以跟一下源码,此处不展开了。
@SuppressWarnings("rawtypes")
@Override
// see original
// https://github.com/Netflix/ocelli/blob/master/ocelli-core/
// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.java
public Mono> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
六、结尾
以上即为Eureka相关内容,感谢阅读。