SpringCloud入门(eureka集群和feign负载均衡)

什么是springcloud?

这是spring官网对springcloud的介绍,大致意思就是:让分布式系统简单化。springcloud是建立在springboot之上的,也就是说他是需要依赖springboot的,因此学习springcloud首先就要了解springboot。在上一篇文章中有介绍到springboot。

这张图说明了Spring Cloud是实施微服务的一系列套件,包括:服务注册与发现、断路器、服务状态监控、配置管理、智能路由、一次性令牌、全局锁、分布式会话管理、集群状态管理等。

下面就用IDEA搭建springcloud项目,来看看springcloud是如何集成这些组件。

搭建一个小型的springcloud项目

1.首先使用IDEA创建一个maven父工程,在pom.xml里引入需要的jar包依赖,dependencyManagement用来管理jar包版本。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.0.4</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.31</version>
            </dependency>

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.然后创建子工程,首先创建一个provider工程,作为服务提供者。

右键选中父工程后new一个module,选择maven就可以新建一个子工程了。

每创建一个新工程首先在pom.xml里添加一些基础的依赖。

 <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId><!--简化javabean开发的jar,用注解的方式快速写setter,getter,-->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- actuator监控信息完善 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId><!--eureka组件,后面没带server,即为客户端client-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.9.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.0.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId><!--因该是将javabean变为配置文件的jar包-->
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
    </dependencies>

接着是编写application.yml文件

server:
  port: 8001
spring:
  application:
    name: userapi       #服务提供者名字,最终会显示在eureka注册中心的名字上,消费者通过这个名字找到服务并调用
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testspringboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource  #阿里巴巴的连接池
    dbcp2:
      max-total: 5                                 #最大维持连接数
      min-idle: 5                                   #最小维持连接数
      initial-size: 5                               #初始化大小
      max-wait-millis: 200                           #等待连接获取的最大等待时间
  jpa:
    hibernate:
      ddl-auto: update                              #若数据库中有表,则不创建。
    show-sql: true

这里的持久层框架使用的是SpringJPA,比较方便。

下面看下provide工程的目录结构:

 这里provider8001代表是运行在8001端口上的服务提供者,后面要实现负载均衡还会建立多个服务提供者。

注意每个子工程都是一个独立的springboot项目,因此每次新建完一个子module都要编写springboot的启动类。且启动类的位置要包含其他所有的包,这样这个启动类在能在启动时加载其他的需要放入spring容器中的bean,也就是对其他的包进行管理。

启动类的通用写法:

package com.tellhow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
public class ProviderApplicationRun8001 {
    public static void main(String args[]){
        SpringApplication.run(ProviderApplicationRun8001.class,args);
    }
}

controller层代码:

package com.tellhow.controller;

import com.tellhow.repository.UserRepository;
import com.tellhow.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("test")
    public String testProvider(){
        return "Hello world";
    }

    @RequestMapping(value = "/users",method = RequestMethod.GET)
    public List<User> getUsers(){
        return userRepository.findAll();
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public boolean addUser(User user){
        userRepository.save(user);
        return true;
    }

    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public boolean updateUser(User user){
        userRepository.save(user);//如果主键存在则修改。
        return true;
    }


}

主要就是查询数据库里的user列表,用来展示分布式调用的。持久层实现参见上一篇文章springboot整合jpa.

服务层就大致写完了,写完之后需要验证一下是否正常可用,保证下写消费者调用时能正常运行。

访问http://localhost:8001/user/users 运行结果:

代表服务消费者可以正常启动,下面就可以来写消费者了。

1.与建立服务提供者同样的操作,右键父工程,新建module,添加pom.xml依赖,编写applicaton.yml文件。编写启动类

applicaton.yml文件里只需要配置一个端口即可

server:
  port: 80

启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;


@SpringBootApplication
@ComponentScan(basePackages = {"com.tellhow"})//指定启动时扫描哪些需要管理的包,若将启动类放在需要管理的包的上一级,则不需要此注解
public class ConsumerApplicationRun {
    public static void main(String args[]){
        SpringApplication.run(ConsumerApplicationRun.class,args);
    }
}

conctroller层:通过RestTemplate调用服务提供者。由此可以看出,springcloud的分布式调用,是用rest来进行调用,不同于dubbo的RPC调用。

使用RestTemplate之前需要将其注入到spring容器中。

package com.tellhow.cfgBean;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RetryRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

controller:

package com.tellhow.controller;

import com.tellhow.beans.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    public RestTemplate restTemplate;

    public static final String PREX="http://localhost:8001";
    //public static final String PREX="http://USERAPI";//通过eureka上注册的服务名来调用

    @RequestMapping(value = "/consumer/user",method = RequestMethod.GET)
    public List<User> getUsers(){
        return restTemplate.getForObject(PREX+"/user/users",List.class);
    }
}

然后先启动服务提供者,再启动服务消者,查看结果,因为消费者工程的端口后是80,因此可以访问地址可以省略端口

访问:http://localhost/consumer/user

可以看到,成功调用。

加入eureka注册中心

eureka是用来做服务注册与发现的,也就是服务提供者将服务注册到eureka注册中心,然后消费者在注册中心对服务进行调用消费。例如:想要开淘宝店需要先到淘宝平台注册店铺,然后买家才可以在淘宝上找到该店铺进行消费购买。

当然eureka还有很多功能:通过心跳检测、健康检查、客户端缓存等机制,保证了系统具有高可用和灵活性。

接下来建立一个eureka的子工程。建立module的过程不做赘述了。

pom.xml依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId><!--eureka 服务-->
        </dependency>
    </dependencies>

application.yml

server:
  port: 7000
eureka:
  instance:
    hostname: eureka7000.com  #eureka主机名
  client:
    register-with-eureka: false   #表示不向注册中心注册自己
    fetch-registry: false          # 表示自己端就是注册中心,职责就是维护服务实例,不需要检索服务。
    service-url:
      defaultZone: http://eureka7001.com:7000/eureka/

启动类:

package com.tellhow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer         //Eureka服务类,接受其他服务注册进来。
public class EurekaApplicationRun {
    public static void main(String args[]){
        SpringApplication.run(EurekaApplicationRun.class,args);
    }
}

如此,eureka注册中心就建立完成了,之后需要在privoder工程里添加向注册中心注册的相关东西。pom.xml添加eureka依赖

首先provider工程启动类上添加注解:@EnableEurekaClient //启动eureka客户端

然后application.yml改为如下:

server:
  port: 8001
spring:
  application:
    name: userapi       #服务提供者名字,最终会显示在eureka注册中心的名字上,消费者通过这个名字找到服务并调用
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testspringboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource  #阿里巴巴的连接池
    dbcp2:
      max-total: 5                                 #最大维持连接数
      min-idle: 5                                   #最小维持连接数
      initial-size: 5                               #初始化大小
      max-wait-millis: 200                           #等待连接获取的最大等待时间
  jpa:
    hibernate:
      ddl-auto: update                              #若数据库中有表,则不创建。
    show-sql: true
eureka:
  client:
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka  #后面必须加个eureka 才能注册上去。

  instance:
    instance-id: userAPI-8001 #注册在eureka上的服务的实例的id
    prefer-ip-address: true    #显示IP地址,方便查找
info:
  app.name: TestMaven
  company.name: www.tellhow.com
  .artifactId: $project.artifactId$
  buibuildld.version: $project.version$

消费者工程里也做相似的改动:启动类添加注解:

@EnableEurekaClient //eureka是服务注册中心,所以对用向服务中心注册服务的,和向服务中心消费服务的,都是eureka客户端

application.yml

server:
  port: 80
eureka:
  client:
    register-with-eureka: false #消费者 不向服务端注册
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka/

修改完成后先启动注册中心工程,再启动服务提供者工程,接着是消费者。

启动完成后访问:http://127.0.0.1:7000如果能出现一下画面则代表eureak注册中心工程建立成功。

 eureka服务中心能正常启动后,将consumer项目controller里的访问连接前缀由http://localhost:8001换为:http://USERAPI,也就是通过服务端注册在服务中心的id来找到要调用的服务。

        用到服务中心之后,有一个问题,就是当服务中心挂掉之后,那么消费者便无法找到要调用的服务了,系统就挂了。为了解决此问题,就可以用到eureka的集群,也就是高可用。

eureka集群非常简单,只需要按照之前建立eureka项目的步骤再多建立几个eureka注册中心就ok了,我这里再建立个,并演示集群eureka的application.yml文件该怎么写

#第一个eureka的yml文件:
server:
  port: 7000
eureka:
  instance:
    hostname: eureka7000.com  #eureka主机名
  client:
    register-with-eureka: false   #表示不向注册中心注册自己
    fetch-registry: false          # 表示自己端就是注册中心,职责就是维护服务实例,不需要检索服务。
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/


#第二个eureka的yml文件:
server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka/,http://eureka7002.com:7002/eureka/

#第三个eureka的yml文件:
server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002.com
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7000.com/eureka/,http://eureka7001.com/eureka/


然后将消费端和生产者的yml文件里的defaultZone都加上上面的访问地址,便可以将服务注册进三个注册中心了,这样某一个注册中心挂掉的话对整个系统没有任何影响。

现在注册中心已经高可用了,但又有一个新的问题,如果服务提供者挂了,那么系统也就挂了。所以也需要实现服务提供者的高可用,实现步骤与实现eureka高可用的步骤一样,建立多个服务提供者,注册进三个注册中心即可。步骤就省略了。

那么有了三个服务提供者,每次消费者在eureka上调用服务时,由哪个服务提供者来提供服务呢,怎样才能做到高效呢。也就是怎样做到负载均衡来提高效率呢。

springcloud提供了两种负载均衡的组件:Ribbon和Feign。

一般我们都使用Feign来做负载均衡,因为Feign本身就包含了Ribbon,而且Feign是使用面向接口编程的方式,更符合大众的方式。

使用一个新组件 第一步pom.xml添加依赖,第二步修改application.yml文件 第三部相关编码。

添加依赖:

<!-- feign相关 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

yml文件不用需改,

在消费者启动类里加入Feign客户端注解。

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerFeignApplication.class,args);
    }
}

编写service接口

package com.tellhow.service;

import com.tellhow.beans.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

@FeignClient(value = "USERAPI")
public interface UserService {

    /**
     * 此接口还是相当于rest请求转发,会去找eureak里USERAPI这个服务里,根据RequestMapping的路径找对应的方法,其实就是找provider里的方法。
     * @return
     */
    @RequestMapping(value = "user/users",method = RequestMethod.GET)
    public List<User> getUsers();
}

controller层:

package com.tellhow.controller;

import com.tellhow.beans.User;
import com.tellhow.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/feign/users")
    public List<User> getUsers(){
        return userService.getUsers();
    }

}

然后依次启动各个项目,访问:

http://localhost/feign/users

就能看到结果啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章