微服务之Ribbon客户端负载均衡,Http远程调用服务,Feign接口方式调用远程服务

1.先单独搭建一个微服务,这个微服务不需要配置文件,专门用来存放实体类与一些公用的配置类

package cn.xrj.springcloud.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Qcpage extends Model<Qcpage> implements Serializable{
    /**
     * 主键id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 职位
     */
    private String job;

    /**
     * 公司
     */
    private String company;

    /**
     * 工作地点
     */
    private String place;

    /**
     * 薪水
     */
    private String salar;

    /**
     * 发布时间
     */
    private String data;

    /**
     * 标记数据来自或存储到哪个数据库
     */
    private String dbsource;
}

注意:@AllArgsConstructor注解表示全参构造方法,@NoArgsConstructor注解表示无参构造方法,@Accessors(chain = true)注解表示允许链式写法。dbsource属性用于标记数据库(微服务架构一般为分布式系统,所以记录数据的来源与去处),后面测试负载均衡时可见效果。

JobInfoService接口与ServiceFallBackFactory实现类用于Feign远程调用与服务降级时使用,目前不说明。

2.服务提供者

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.microsoft</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>cn.xrj.springcloud</groupId>
    <artifactId>springcloud-provider-jobinfo-8001</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-provider-jobinfo-8001</name>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <!--配置info信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--拿到实体类-->
        <dependency>
            <groupId>cn.xrj.springcoud</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <!-- 此配置不可缺,否则mybatis的Mapper.xml将会丢失 -->
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <!--指定资源的位置-->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件:

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 199654
    url: jdbc:mysql://localhost:3306/pachong?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
  application:
    name: springcloud-provider-jobinfo //微服务名

mybatis-plus: //mybatis-plus配置
  mapper-locations: classpath:cn/xrj/springcloud/mapper/*/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: cn.xrj.springcloud.pojo

server:  
  port: 8001  //端口号
eureka:
  client:
    service-url:
      defaultZone://需要注册到的eureka集群 http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-jobinfo:8001 //微服务实例名
    hostname: localhost
    prefer-ip-address: true  //是否显示IP地址

info: //这里配置,当点击eureka里的微服务时,跳到相应的info界面,而保护出现error页面
  app.name: springcloud-provider-jobinfo
  company.name: richinfo
  author.name: xingrenjie

Controller层:

package cn.xrj.springcloud.controller;



import cn.xrj.springcloud.service.QcpageService;
import cn.xrj.springcloud.pojo.Qcpage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * <p>
 * 51job招聘信息表 前端控制器
 * </p>
 *
 * @author xrj
 * @since 2019-12-13
 */
@RestController
public class QcpageProviderController {
    @Autowired
    QcpageService qcpageService;

    @Autowired
    DiscoveryClient client;

    @GetMapping("/qcpage/getAll")
    public List<Qcpage> getAll(){
        List<Qcpage> list= qcpageService.list();
        return list;
    }

    @GetMapping("/qcpage/getById/{id}")
    public Qcpage getById(@PathVariable("id")Integer id){
        Qcpage qcpage=qcpageService.getById(id);
        return qcpage;
    }

    @DeleteMapping("/qcpage/deleteById/{id}")
    public boolean deleteById(@PathVariable("id")Integer id){
        boolean flag=qcpageService.removeById(id);
        return flag;
    }

    //获取注册进来的微服务,可以获得一些微服务信息,服务发现
    @GetMapping("/qcpage/getservice")
    public Object getService(){
        List<String> service=client.getServices();
        System.out.println("service:"+service);
        List<ServiceInstance> instances=client.getInstances("springcloud-provider-jobinfo");
        for (ServiceInstance instance : instances) {
            System.out.println("serviceId:"+instance.getServiceId());
            System.out.println("InstanceId:"+instance.getInstanceId());
            System.out.println("host:"+instance.getHost());
            System.out.println("port:"+instance.getPort());
        }
        return this.client;
    }
}

启动类:

@SpringBootApplication
@MapperScan("cn.xrj.springcloud.mapper")
@EnableEurekaClient
@EnableDiscoveryClient //服务发现~
public class SpringcloudProviderJobinfo8001Application {

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

}

其中:@EnableEurekaClient注解开启eureka客户端,@EnableDiscoveryClient注解开启服务发现,服务提供者跟普通的controller没什么区别!!,如果做集群,那么复制一份即可,如果不同服务器则端口需改!!!

3.服务消费者,开启Ribbon负载均衡,http远程调用服务

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.microsoft</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>cn.xrj.springcloud</groupId>
    <artifactId>springcloud-consumer-jobinfo-80</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-consumer-jobinfo-80</name>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.xrj.springcoud</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <!-- 此配置不可缺,否则mybatis的Mapper.xml将会丢失 -->
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <!--指定资源的位置-->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件:

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

Controller层:

package cn.xrj.springcloud.controller;

import cn.xrj.springcloud.pojo.Qcpage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class QcpageConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    //以下注释常量为未使用ribbon负载均衡时配置的,只能调用某个微服务的一个实例,真实场景是某个微服务做了集群配置,所以此方法不用
//    private static final String PRIFIX="http://localhost:8001";

    //开启负载均衡后,主机名与端口号使用服务名代替,在调用时,则轮询调用某微服务下所有集群实例
    private static final String PRIFIX="http://SPRINGCLOUD-PROVIDER-JOBINFO";

    @RequestMapping("/consumer/jobinfo/getById/{id}")
    public Qcpage getById(@PathVariable("id")Integer id){
        return restTemplate.getForObject(PRIFIX+"/qcpage/getById/"+id,Qcpage.class);
    }

    @RequestMapping("/consumer/jobinfo/getAll")
    public List<Qcpage> getAll(){
        return restTemplate.getForObject(PRIFIX+"/qcpage/getAll",List.class);
    }

    @RequestMapping("/consumer/jobinfo/deleteById/{id}")
    public void deleteById(@PathVariable("id")Integer id){
         restTemplate.delete(PRIFIX+"/qcpage/deleteById/{id}",id);
    }
}

Ribbon配置及开启(默认配置轮询机制,可自定义配置,后续加上):

@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced //此注解开启请求时使用ribbon负载均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

启动类:

@SpringBootApplication( exclude= {DataSourceAutoConfiguration.class})配置此属性为了避免自动配置数据源,因为此服务为消费者,本身不需要数据源
@EnableEurekaClient//开启eureka客户端
public class SpringcloudConsumerJobinfo80Application {

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

}

如此,启动eureka集群,启动服务提供者和消费者,访问消费者接口,则会远程调用服务提供者接口获取数据,因为开启了ribbon客户端负载均衡,所以,获取的数据的dbsource属性会有不同的值,表示来自不同的数据库。

4.服务消费者改用Feign接口方式调用服务(更符合面向接口编程,底层集成了Ribbon来实现负载均衡)

消费者的pom文件和api服务里导入依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

由于底层集成了ribbon,所以ribbon还是要开启,配置简单。

然后在上面搭建的api这个微服务里新建service包,里面新建一个接口:

package cn.xrj.springcloud.service;

import cn.xrj.springcloud.pojo.Qcpage;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.List;

@FeignClient(value = "springcloud-provider-jobinfo")
@Component
public interface JobInfoService {
    @GetMapping("/qcpage/getAll")
    List<Qcpage> getAll();

    @GetMapping("/qcpage/getById/{id}")
    Qcpage getById(@PathVariable("id")Integer id);

    @DeleteMapping("/qcpage/deleteById/{id}")
    boolean deleteById(@PathVariable("id")Integer id);
}

此接口使用@FeignClient(value = "springcloud-provider-jobinfo")注解,表示此接口是一个Feign客户端,并调用的服务为名为springcloud-provider-jobinfo的微服务,里面包含三个抽象方法,对应了springcloud-provider-jobinfo微服务下的三个接口。注意,方法名可以不同,但返回值和参数类型必须相同,且接口路径和请求方式必须一一对应,用于标识哪个方法对应哪个接口。

消费者controller调用:

package cn.xrj.springcloud.controller;

import cn.xrj.springcloud.pojo.Qcpage;
import cn.xrj.springcloud.service.JobInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class QcpageConsumerController {
    @Autowired
    JobInfoService jobInfoService;

    @RequestMapping("/consumer/jobinfo/getById/{id}")
    public Qcpage getById(@PathVariable("id")Integer id){
        return jobInfoService.getById(id);
    }

    @RequestMapping("/consumer/jobinfo/getAll")
    public List<Qcpage> getAll(){
        return jobInfoService.getAll();
    }

    @RequestMapping("/consumer/jobinfo/deleteById/{id}")
    public boolean deleteById(@PathVariable("id")Integer id){
         return jobInfoService.deleteById(id);
    }
}

到此,feign远程调用服务结束,是不是更加符合面向接口编程的思想!!也更方便实用!也很容易理解。

猜你喜欢

转载自blog.csdn.net/weixin_46792649/article/details/106317877