Springcloud与Consul的整合

7 篇文章 0 订阅
5 篇文章 0 订阅
本文详细介绍了如何使用Spring Cloud Consul搭建服务提供者和服务消费者,包括配置、依赖、实例创建、服务注册与发现、负载均衡等。通过RestTemplate或FeignClient实现服务调用,并展示了健康检查、多实例部署与负载均衡测试。
摘要由CSDN通过智能技术生成

一、首先创建一个用以提供服务的Springcloud客户端service-user.

1. 依赖

    implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

其中'org.springframework.boot:spring-boot-starter-actuator'是必要的,若无则会造成consul服务健康检查失败。

同时需要说明一点,由于我们打算将服务提供者多实例部署,为方便起见,我们需要将程序打包,所以我们要配置一下打包插件。

jar {
    baseName = 'service-user'
    version = '1.0.0'
    manifest {
        attributes "Manifest-Version": 1.0,
                'Main-Class': 'com.chris.user.UserApplication'
    }
}

2.配置文件application.yml

server:
  port: 8001
spring:
  application:
    name: service-user
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        enabled: true
        register: true
        instance-id: ${spring.application.name}-${instance-id-suffix}
        service-name: ${spring.application.name}
        prefer-ip-address: true
        ip-address: 192.168.0.100
instance-id-suffix: 01

简单清晰,我们在本地启动了一个consul,所以host是localhost,由于我们要测试负载均衡,准备启动多个服务提供者实例,所以在实例后面加不同的后缀用以区分。

3.启动类。我们的启动类就是根据向导自动生成的,没有添加任何东西。

package com.chris.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UserApplication {

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

}

4. 数据模型UserModel,需要实现序列化接口。

package com.chris.user.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:17
 * Use for:
 * Explain:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserModel implements Serializable {
    private String username;
    private int age;
    private String address;
    private String job;
}

5. 一个通用的接口,用以在Controller和Service同步实现,甚至在服务消费者那里也需要复制使用。

补充一句,数据模型和服务接口很重要,这是RPC进行产销对接很重要的两个类,像Dubbo不仅要求数据结构相同,连包名都要相同,而我们这里仅仅要求数据结构相同,方便数据传输前后的编解码。建议合适的方式是单独封装为通用的库,给服务产销双方引用。

package com.chris.user.common;

import com.chris.user.model.UserModel;

import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:19
 * Use for:
 * Explain:
 */
public interface UserApi {
    List<UserModel> findAll();

    UserModel findById(int id);

    String port();
}

我这里设计三个方法是有用意的:

fandAll()方法测试无参数RPC调用;

findById()测试有参数RPC调用;

port()方法则是用来测试负载均衡的。多实例部署在本地,要求端口不同,我们根据端口号可以看出负载均衡的效果。

6. service继承和实现

package com.chris.user.service;

import com.chris.user.common.UserApi;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:19
 * Use for:
 * Explain:
 */
public interface UserService extends UserApi {

}
package com.chris.user.service;

import com.chris.user.model.UserModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:20
 * Use for:
 * Explain:
 */
@Service
public class UserServiceImpl implements UserService {
    @Value("${server.port}")
    int port;

    @Override
    public List<UserModel> findAll() {
        return Arrays.asList(
                new UserModel("Chris", 42, "上海", "Java开发"),
                new UserModel("Mike", 24, "北京", "销售"),
                new UserModel("Rose", 26, "西安", "HR"),
                new UserModel("John", 36, "广州", "总经理助理"),
                new UserModel("Marry", 42, "深圳", "运营")
        );
    }

    @Override
    public UserModel findById(int id) {
        return new UserModel("Billbo", 100, "夏尔", "魔戒持有者");
    }

    @Override
    public String port() {
        return "Port: " + port;
    }
}

先继承后实现不是必要的,只是我们假设通用接口UserApi是底层提供给我们的,我们可能要对通用接口进行扩展,所以先继承。

7. controller实现。也可先继承后实现。

package com.chris.user.web;

import com.chris.user.common.UserApi;
import com.chris.user.model.UserModel;
import com.chris.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:27
 * Use for:
 * Explain:
 */
@RestController
@RequestMapping("api/user")
public class UserController implements UserApi {
    @Autowired
    UserService userService;

    @GetMapping("list")
    @Override
    public List<UserModel> findAll() {
        return userService.findAll();
    }

    @GetMapping("get")
    @Override
    public UserModel findById(int id) {
        return userService.findById(id);
    }

    @GetMapping("port")
    @Override
    public String port() {
        return userService.port();
    }
}

看起来,controller像是给service做了一个代理。这只是一个简单的示例。

二、创建一个服务消费者端service-worker。

依赖跟服务提供者一样。我们还需要把在服务提供者工程创建好的数据模型UserModel和通用接口UserApi复制过来。

1. 配置文件application.yml。配置文件跟服务提供者也是一样的,只需要修改一下spring.application.name这个参数就可以了。

server:
    port: 8011
spring:
    application:
        name: service-worker
    cloud:
        consul:
            host: localhost
            port: 8500
            discovery:
                enabled: true
                register: true
                instance-id: ${spring.application.name}-${instance-id-suffix}
                service-name: ${spring.application.name}
                prefer-ip-address: true
                ip-address: 192.168.0.100
instance-id-suffix: 01

2. 启动类。

在这里就要说明一下。我们实现服务消费者的RPC远程调用有两种思路,一种是通过RestTemplate来发出请求,一种是通过FeignClient来实现。这里我们先实现第一种。所以我们需要先创建RestTemplate的Bean,我们打算在启动类创建,当然也可以在别处创建。

package com.chris.worker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class WorkerApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

 @LoadBalanced用来实现负载均衡。

3. service实现类。

package com.chris.worker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/21 16:00
 * Use for:
 * Explain:
 */
@Service
public class UserService implements UserApi {
    @Autowired
    RestTemplate restTemplate;

    @Override
    public List<UserModel> findAll() {
        ResponseEntity<List<UserModel>> responseEntity = restTemplate.exchange(
                "http://service-user/api/user/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<UserModel>>() {
                });
        return responseEntity.getBody();
    }

    @Override
    public UserModel findById(int id) {
        ResponseEntity<UserModel> responseEntity = restTemplate.exchange(
                "http://service-user/api/user/get?id=" + id,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<UserModel>() {
                });
        return responseEntity.getBody();
    }

    @Override
    public String port() {
        ResponseEntity<String> responseEntity = restTemplate.exchange(
                "http://service-user/api/user/port",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<String>() {
                });
        return responseEntity.getBody();
    }
}

4. 控制层实现类。

package com.chris.worker.web;

import com.chris.worker.common.UserApi;
import com.chris.worker.model.UserModel;
import com.chris.worker.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:27
 * Use for:
 * Explain:
 */
@RestController
@RequestMapping("api/user")
public class UserController implements UserApi {
    @Autowired
    UserService userService;

    @GetMapping("list")
    @Override
    public List<UserModel> findAll() {
        return userService.findAll();
    }

    @GetMapping("get")
    @Override
    public UserModel findById(int id) {
        return userService.findById(id);
    }

    @GetMapping("port")
    @Override
    public String port() {
        return userService.port();
    }
}

至此我们的工程都完成了。

下面说一下使用FeignClient来实现的区别。

区别一,是需要添加对FeignClient的依赖

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

区别二,不需要创建TestTemplate的Bean。

区别三,就是service,我们不需要去实现UserApi,继承后覆写所有方法,然后加上几个注解就好了。

package com.chris.worker.service;

import com.chris.worker.common.UserApi;
import com.chris.worker.model.UserModel;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
 * @author Chris Chan
 * Create on 2021/4/22 13:19
 * Use for:
 * Explain:
 */
@FeignClient("service-user")
public interface UserService extends UserApi {
    @GetMapping("/api/user/list")
    @Override
    List<UserModel> findAll();

    @GetMapping("/api/user/get")
    @Override
    UserModel findById(@RequestParam("id") int id);

    @GetMapping("/api/user/port")
    @Override
    String port();
}

类上的注解指明服务提供者的服务名,方法注解则明确了请求方式、接口路径,参数表要加@RequestParam()注解。

控制层不用变。

三、测试

1. 本地运行consul一个开发模式的服务器

consul agent -dev

查看页面

http://localhost:8500/

2. 服务提供者端打包

gradle build

完成之后,在build/libs/下可以看到我们打好的包。

3. 我们打开多个终端,通过命令,运行多个服务提供者。

java -jar build/libs/consul-user-demo-20210422-0.0.1-SNAPSHOT.jar --server.port=8001 --instance-id-suffix=01
java -jar build/libs/consul-user-demo-20210422-0.0.1-SNAPSHOT.jar --server.port=8002 --instance-id-suffix=02
java -jar build/libs/consul-user-demo-20210422-0.0.1-SNAPSHOT.jar --server.port=8003 --instance-id-suffix=03

我们在运行时修改了服务端口号和实例ID的后缀。看看页面效果。consul进行玩健康检查后,把所有的实例都展示出来。

4. 我们在运行服务消费者端,我们只运行一个,直接IDE启动就好了。

5. 访问服务消费者端

http://localhost:8011/api/user/list

 

http://localhost:8011/api/user/get?id=1

6.测试负载均衡

http://localhost:8011/api/user/port

不断刷新,会看到端口号不断在发生变化。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值