极简版thrift+consul

11 篇文章 0 订阅
3 篇文章 0 订阅

一、吐槽

之前写过一期这个主题,然后被墙外某网站原封不动的抄袭了…但问题是上一期代码有问题,就删了准备重写来着。希望抄袭的人能同步更新这一篇吧…

二、准备

docker起一个consul

docker pull docker.io/consul

然后

docker run -d --name test_consul -p 8500:8500 docker.io/consul

服务提供方

先编译一个Hello.java的thrift服务

idl如下:

namespace java com.yanyu.service.rpc
service Hello{
	string sayHello(1:string param)
}

运行

thrift -r -gen java Hello.thrift

即可生成Hello.java,这个就是我们的服务

然后新建一个maven项目

依赖如下:
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    </dependencies>

其中springboot版本为2.4.1,cloud版本为2020.0.1

配置
server:
  port: 8003
spring:
  application:
    name: consul-thrift-provider
  cloud:
    consul:
      port: 8500
      host: localhost
      discovery:
        service-name: ${spring.application.name}
rpc:
  maxThreads: 10
  minThreads: 1
  delay: 10
  thrift:
    port: 8013
    name: thrift-service
正式开始写代码
启动器
@SpringBootApplication
@EnableDiscoveryClient
public class AppPro {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(AppPro.class, args);
        ThriftServer thriftServer = run.getBean(ThriftServer.class);
        thriftServer.start();
    }

}

其中ThriftServer还没写,下面写,爆红不要急。

工具类

注册参数实体类:

/**
 * desc 注册参数实体类
 */
@Data
@Builder(toBuilder = true)
public class RegisterParameter {
    private String Name;
    private String Id;
    private String[] Tags;
    private String Address;
    private int Port;
}

注册机:

@Component
public class ConsulRegister {

    public static final String path = "/v1/agent/service/register";
    @Resource
    private RestTemplate restTemplate;

    public void registe(RegisterParameter registerParameter, String consulAddress){
        restTemplate.put(consulAddress, JSON.toJSONString(registerParameter));
    }
}

整体思路是发送put请求至consul的注册接口。这里需要先定义restTemplate,不然无法注入:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
服务实现
@Service
public class HelloServiceImpl implements Hello.Iface {

    @Override
    public String sayHello(String param) throws TException {
        return String.format("hello: %s, it's my provider!", param);
    }
}
注册
@Slf4j
@Component
public class ThriftServer {

    @Value("${spring.cloud.consul.host}")
    private String consulHost;
    @Value("${spring.cloud.consul.port}")
    private int consulPort;
    /**
     * thrift服务端口
     */
    @Value("${rpc.thrift.port}")
    private int port;
    @Value("${rpc.thrift.name}")
    /**
     * thrift服务名
     */
    private String thriftName;
    @Value("${rpc.minThreads}")
    private int minThreads;
    @Value("${rpc.maxThreads}")
    private int maxThreads;
    /**
     * thrift服务的地址
     */
    private final String hostIp = InetAddress.getLocalHost().getHostAddress();
    @Resource
    private ConsulRegister consulRegister;

    // 协议工厂
    private TBinaryProtocol.Factory protocolFactory;
    // 传输工厂
    private TTransportFactory transportFactory;
    /**
     * 实际提供服务的类
     */
    @Resource
    private HelloServiceImpl helloService;

    public ThriftServer() throws UnknownHostException {
    }
    public void init() {
        protocolFactory = new TBinaryProtocol.Factory();
        transportFactory = new TTransportFactory();
    }
    public void start() {
        Hello.Processor processor = new Hello.Processor<Hello.Iface>(helloService);
        init();
        try {
            TServerTransport transport = new TServerSocket(port);
            TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(transport);
            tArgs.processor(processor);
            tArgs.protocolFactory(protocolFactory);
            tArgs.transportFactory(transportFactory);
            tArgs.minWorkerThreads(minThreads);
            tArgs.maxWorkerThreads(maxThreads);
            TServer server = new TThreadPoolServer(tArgs);
            log.info("thrift服务启动成功, 地址=http://{}", hostIp + ":" + port);
            String consulInfo = String.format("http://%s:%d%s", consulHost, consulPort, ConsulRegister.path);
            log.info(String.format("consulInfo is %s", consulInfo));
            consulRegister.registe(
                    RegisterParameter.builder().Name(thriftName).Port(port).Address(hostIp).Id(thriftName + "-" + port)
                            .build(), consulInfo);
            server.serve();
        } catch (Exception e) {
            log.error("服务启动失败,", e);
        }
    }
}

启动起来,就能看到:
成功
没有报错就是成功

消费方

依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    </dependencies>

配置文件

server:
  port: 9003
spring:
  application:
    name: consul-thrift-consumer
  cloud:
    consul:
      port: 8500
      host: localhost
      discovery:
        service-name: ${spring.application.name}
rpc:
  maxThreads: 10
  minThreads: 1
  delay: 10
  thrift:
    port: 8013
    host: localhost

启动器

@SpringBootApplication
@EnableDiscoveryClient
public class ConApp {

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

}

开始

定义服务实体类
@Data
public class ServiceEntity {

    String Id;
    String Service;
    String[] Tags;
    Meta Meta;
    int Port;
    String Address;
    TaggedAddresses TaggedAddresses;
    Weights Weights;
    boolean EnableTagOverride;
    String Datacenter;
}
@Data
class Meta {

    boolean secure;
}
@Data
class TaggedAddresses{
    lan_ipv4 lan_ipv4;
    wan_ipv4 wan_ipv4;
}
@Data
class lan_ipv4{
    String Address;
    int Port;
}
@Data
class wan_ipv4{
    String Address;
    int Port;
}
@Data
class Weights{
    int Passing;
    int Warning;
}
定义restTemplate

同上

定义一个读取所有服务的类

其实不用读取所有服务,应该只要那一个服务的,但是我懒得整了…

@Service
@Slf4j
public class GetAllConsulService {

    @Resource
    private RestTemplate restTemplate;

    public Map<String, ServiceEntity> allServiceJson(){
        JSONObject maps = restTemplate.getForObject("http://localhost:8500/v1/agent/services", JSONObject.class);
        Map<String, ServiceEntity> res = new HashMap<>();
        for(String key : maps.keySet()){
            res.put(key, JSON.parseObject(JSON.toJSONString(maps.getJSONObject(key)), ServiceEntity.class));
        }
        return res;
    }
}

这个实现丑是丑了点,不过功能海星

调用Thrift的Client
public class HelloClient {
    private Hello.Client helloService;
    private TBinaryProtocol protocol;
    private TSocket transport;
    private String host;
    private int port;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public void init(){
        transport = new TSocket(host, port);
        protocol = new TBinaryProtocol(transport);
        helloService = new Hello.Client(protocol);
    }

    public Hello.Client getHelloService(){
        return helloService;
    }
    public void open() throws TTransportException {
        transport.open();
    }

    public void close(){
        transport.close();
    }
}

把HelloClient托管于Spring:

@Component
public class HelloConfig {
    @Resource
    private GetAllConsulService getAllConsulService;

    @Bean(initMethod = "init")
    public HelloClient helloClient(){
        HelloClient helloClient = new HelloClient();
        helloClient.setHost(getAllConsulService.allServiceJson().get("thrift-service").getAddress());
        helloClient.setPort(getAllConsulService.allServiceJson().get("thrift-service").getPort());
        return helloClient;
    }
}
验证

写一个controller调用下试试:

@RestController
@RequestMapping("/")
@Slf4j
public class HelloController {

    @Autowired
    private HelloClient helloClient;

    @GetMapping
    public String getHello(String name){
        try {
            helloClient.open();
            return helloClient.getHelloService().sayHello(name);
        }catch (Exception e){
            log.error("error: ", e.getMessage());
        }finally {
            helloClient.close();
        }

        return "";
    }
}

总结

本文实现了一个极简版的consul+springcloud+thrift的应用,但是可优化的地方还非常非常多,包括注册的安全性,服务的安全检查策略,服务的启动逻辑,服务的获取逻辑,prometheus监控的加入,负载均衡,用netty替换默认的通讯协议等等,你可以自行探索,也可以等我哪天想弄了再写~
欢迎联系:3352336739@qq.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值