Spring Cloud-Erueka服务注册&发现

零、本文纲要

  • 一、服务暴露&调用
  • 二、服务注册&发现
  • 三、服务注册&发现(集群)
  • 四、负载均衡
  • 五、负载均衡部分源码

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服务,进行服务暴露&调用
启动服务.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的基础依赖

    
        
        
            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服务.png

访问:Eureka http://127.0.0.1:8088/

Eureka注册中心.png

此时就可以看到,我们注册中心内部注册的服务实例了(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服务已经注册到注册中心了,但是此时各项服务还是单例的,如果是集群我们还需做些调整。

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、配置负载均衡策略类,实现负载均衡

Ⅰ 基础依赖

        
            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
拦截请求.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> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
    }

六、结尾

以上即为Eureka相关内容,感谢阅读。

你可能感兴趣的:(Spring Cloud-Erueka服务注册&发现)