零、本文纲要
- 一、服务暴露&调用
- 二、服务注册&发现
- 三、服务注册&发现(集群)
- 四、负载均衡
- 五、负载均衡部分源码
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服务的基础依赖一致
<dependencies>
<!--web服务暴露-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis&connector数据库连接-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--spring cloud基础依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--spring cloud依赖管理-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 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服务,进行服务暴露&调用
启动服务.png
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的基础依赖
<dependencies>
<!--eureka server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
②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服务.png
访问:Eureka http://127.0.0.1:8088/
Eureka注册中心.png
此时就可以看到,我们注册中心内部注册的服务实例了(eureka本身也会将自己注册为服务实例)。
- 2、order&user服务注册
①pom.xml基础依赖
Eureka client的基础依赖
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
②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服务已经注册到注册中心了,但是此时各项服务还是单例的,如果是集群我们还需做些调整。
Eureka注册中心.png
三、服务注册&发现(集群)
- 1、IDEA模拟服务集群
Ⅰ 在想要部署集群的服务处右键-√Copy Configuration
Copy Configuration.png
Ⅱ 在Environment-VM options处设置对应的端口号:-Dserver.pot=8083
设置端口号.png
Ⅲ 正常启动服务即可
此时可以看到user服务已经是集群部署的。
userservice集群.png
- 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、配置负载均衡策略类,实现负载均衡
Ⅰ 基础依赖
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
<version>2.3.0</version>
</dependency>
Ⅱ 负载均衡实现类
注意:这种方式注册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
拦截请求.png
此处接着得到:userservice(服务名称)
得到服务名称.png
- 2、BlockingLoadBalancerClient#execute方法
获取服务实现.png
- 3、BlockingLoadBalancerClient#choose方法
此处我们就会得到对应的loadBalancer,也就是负载均衡策略;
以及我们的service实例serviceInstance。
获取loadBalancer和service实例.png
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<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
六、结尾
以上即为Eureka相关内容,感谢阅读。













网友评论