欢送拜访我的GitHub

这里分类和汇总了欣宸的全副原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 前文《五分钟搞懂spring-cloud-square》具体介绍了什么是spring-cloud-square,以及三种实现类型的具体概念,爱入手的您已急不可待想编码体验spring-cloud-square了,本篇咱们就来畅快实战,体验这个spring官网带给咱们的smart client
  • 如题目所述,接下里咱们会将spring-cloud-square提供的三种client都编码体验,总的来说本篇由以下内容形成:
  1. 新建maven工程,名为<font color="blue">spring-cloud-square-tutorials</font>,这是本篇所有利用的父工程,库版本在此工程中对立治理;
  2. 创立子工程<font color="blue">eureka</font>,作为注册核心
  3. 创立子工程<font color="blue">client</font>,放一些专用的数据结构
  4. 创立子工程<font color="blue">provider</font>,身份是服务提供者,接下来的三个用到spring-cloud-square的子工程,都调用provider的服务
  5. 创立子工程<font color="blue">consumer-okhttp</font>,基于spring-cloud-square的<font color="red">okhttp</font>能力做近程调用
  6. 创立子工程<font color="blue">consumer-retrofit-okhttp</font>,基于spring-cloud-square的<font color="red">retrofit + okhttp</font>能力做近程调用
  7. 创立子工程<font color="blue">consumer-retrofit-webflux</font>,基于spring-cloud-square的<font color="red">retrofit + webflux</font>能力做近程调用
  • 上述几个服务的关系如下图:

如何验证

  • 代码写完之后,如何验证性能是否合乎预期呢?本篇采纳单元测试的形式,consumer-okhttp、consumer-retrofit-okhttp、consumer-retrofit-webflux这三个子工程都有本人的单元测试代码,执行通过就意味着代码性能合乎预期了

源码下载

  • 本篇实战中的残缺源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blo...):
名称链接备注
我的项目主页https://github.com/zq2599/blo...该我的项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blo...该我的项目源码的仓库地址,https协定
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该我的项目源码的仓库地址,ssh协定
  • 这个git我的项目中有多个文件夹,本篇的源码在<font color="blue">spring-cloud-square-tutorials</font>文件夹下,如下图红框所示:

版本信息

  • 本篇实战波及到的次要版本状况如下:
  • JDK:1.8.0_291
  • IDEA:2021.1.3 (Ultimate Edition)
  • maven:3.8.1
  • 操作系统:win10 64位
  • springboot:2.4.4
  • spring-cloud:2020.0.2
  • spring-cloud-square:0.4.0-SNAPSHOT

父工程spring-cloud-square-tutorials

  • 父工程名为<font color="blue">spring-cloud-square-tutorials</font>,其pom.xml如下,除了依赖库的版本在此对立治理,还要留神的是两个仓库的引入(https://repo.spring.io/snapshot和https://repo.spring.io/milestone),引入它们是因为spring-cloud-square还在孵化阶段,没有公布到maven地方仓库:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.4.4</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.bolingcavalry</groupId>    <artifactId>spring-cloud-square-tutorials</artifactId>    <version>1.0-SNAPSHOT</version>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>        <java.version>1.8</java.version>        <spring-cloud.version>2020.0.2</spring-cloud.version>        <square.dependency.version>0.4.0-SNAPSHOT</square.dependency.version>    </properties>    <packaging>pom</packaging>    <description>Demo project for Spring Cloud Square Retrofit Web</description>    <modules>        <module>provider</module>        <module>eureka</module>        <module>consumer-okhttp</module>        <module>client</module>        <module>consumer-retrofit-okhttp</module>        <module>consumer-retrofit-webflux</module>    </modules>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>            <dependency>                <groupId>com.squareup.okhttp3</groupId>                <artifactId>okhttp</artifactId>                <version>3.14.9</version>                <scope>compile</scope>            </dependency>            <dependency>                <groupId>ch.qos.logback</groupId>                <artifactId>logback-classic</artifactId>                <version>1.1.7</version>            </dependency>            <dependency>                <groupId>org.projectlombok</groupId>                <artifactId>lombok</artifactId>                <version>1.16.16</version>            </dependency>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-square-okhttp</artifactId>                <version>${square.dependency.version}</version>            </dependency>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-square-retrofit</artifactId>                <version>${square.dependency.version}</version>            </dependency>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-boot-starter-webflux</artifactId>                <version>${square.dependency.version}</version>            </dependency>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-square-retrofit-webclient</artifactId>                <version>${square.dependency.version}</version>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <plugins>            <plugin>                <!--skip deploy (this is just a test module) -->                <artifactId>maven-deploy-plugin</artifactId>                <configuration>                    <skip>true</skip>                </configuration>            </plugin>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>    <repositories>        <repository>            <id>spring-snapshots</id>            <name>Spring Snapshots</name>            <url>https://repo.spring.io/snapshot</url>            <snapshots>                <enabled>true</enabled>            </snapshots>        </repository>        <repository>            <id>spring-milestones</id>            <name>Spring Milestones</name>            <url>https://repo.spring.io/milestone</url>            <snapshots>                <enabled>false</enabled>            </snapshots>        </repository>    </repositories>    <pluginRepositories>        <pluginRepository>            <id>spring-snapshots</id>            <name>Spring Snapshots</name>            <url>https://repo.spring.io/snapshot</url>            <snapshots>                <enabled>true</enabled>            </snapshots>        </pluginRepository>        <pluginRepository>            <id>spring-milestones</id>            <name>Spring Milestones</name>            <url>https://repo.spring.io/milestone</url>            <snapshots>                <enabled>false</enabled>            </snapshots>        </pluginRepository>    </pluginRepositories></project>

注册核心eureka

  • eureka利用并没有什么特别之处,pom.xml如下:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud-square-tutorials</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>eureka</artifactId>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <start-class>com.bolingcavalry.eureka.EurekaApplication</start-class>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>        </dependency>    </dependencies>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId><!--                <version>Finchley.BUILD-SNAPSHOT</version>-->                <version>${spring-cloud.version}</version>                <type>pom</type>                <scope>import</scope>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <!-- defined in spring-cloud-starter-parent pom (as documentation hint),                    but needs to be repeated here -->                <configuration>                    <requiresUnpack>                        <dependency>                            <groupId>com.netflix.eureka</groupId>                            <artifactId>eureka-core</artifactId>                        </dependency>                        <dependency>                            <groupId>com.netflix.eureka</groupId>                            <artifactId>eureka-client</artifactId>                        </dependency>                    </requiresUnpack>                </configuration>            </plugin>            <plugin>                <!--skip deploy (this is just a test module) -->                <artifactId>maven-deploy-plugin</artifactId>                <configuration>                    <skip>true</skip>                </configuration>            </plugin>        </plugins>    </build></project>
  • 中规中矩的配置文件application.yml,端口是8761,前面的利用也要保持一致:
server:  port: 8761spring:  application:    name: eurekaeureka:  client:    registerWithEureka: false    fetchRegistry: false  server:    waitTimeInMsWhenSyncEmpty: 0
  • 启动类EurekaApplication.java,记得用注解EnableEurekaServer开启eureka服务:
package com.bolingcavalry.eureka;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication@EnableEurekaServerpublic class EurekaApplication {    public static void main(String[] args) throws Exception {        SpringApplication.run(EurekaApplication.class, args);    }}
  • eureka利用曾经实现,接下来是服务提供者了

服务提供者provider

  • -新建名为<font color="blue">provider</font>的利用,pom.xml如下,可见是个一般的web工程,会将本人注册到eureka下来:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud-square-tutorials</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>provider</artifactId>    <packaging>jar</packaging>    <dependencies>        <dependency>            <groupId>com.bolingcavalry</groupId>            <artifactId>client</artifactId>            <version>${project.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-context</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <!-- 如果父工程不是springboot,就要用以下形式应用插件,能力生成失常的jar -->            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>com.bolingcavalry.provider.ProviderApplication</mainClass>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 配置文件application.yml:
spring:  application:    name: providerserver:  port: 18080eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/
  • 启动类ProviderApplication .java:
package com.bolingcavalry.provider;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ProviderApplication {    public static void main(String[] args) {        SpringApplication.run(ProviderApplication.class, args);    }}
  • web服务类,可见对外提供了两个接口<font color="blue">hello-str</font>和<font color="blue">hello-obj</font>,前者返回字符串,或者返回对象:
package com.bolingcavalry.provider.controller;import com.bolingcavalry.client.HelloResponse;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.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import java.util.Random;@RestControllerpublic class Hello {    public static final String HELLO_PREFIX = "Hello World";    @Autowired    DiscoveryClient client;    /**     * 随机取一个provider实例,返回其形容信息,     * 如果只有一个provider实例时,返回的就是以后服务信息     * @return     */    private String providerDescription() {        List<ServiceInstance> instances = client.getInstances("provider");        ServiceInstance selectedInstance = instances                .get(new Random().nextInt(instances.size()));        return String.format("serviceId [%s], host [%s], port [%d]",                selectedInstance.getServiceId(),                selectedInstance.getHost(),                selectedInstance.getPort());    }    private String dateStr(){        return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());    }    @GetMapping("/hello-str")    public String helloStr() {        List<ServiceInstance> instances = client.getInstances("provider");        ServiceInstance selectedInstance = instances                .get(new Random().nextInt(instances.size()));        return HELLO_PREFIX                + " : "                + providerDescription()                + ", "                + dateStr();    }        @GetMapping("/hello-obj")    public HelloResponse helloObj(@RequestParam("name") String name) {        return new HelloResponse(name, dateStr(), providerDescription());    }}
  • 这个provider利用算是个最朴实无华的web服务了

启动服务

  • 当初能够将eureka和provider服务先后启动,这样前面的利用编码实现后能够间接测试

consumer-okhttp,基于spring-cloud-square的okhttp能力

  • 接下来要创立的利用<font color="blue">consumer-okhttp</font>,应用的是spring-cloud-square三种能力的第一种:okhttp
  • pom.xml内容如下,重点是spring-cloud-square-okhttp和spring-cloud-starter-loadbalancer这两个库的引入:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud-square-tutorials</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>consumer-okhttp</artifactId>    <packaging>jar</packaging>    <dependencies>        <dependency>            <groupId>com.bolingcavalry</groupId>            <artifactId>client</artifactId>            <version>${project.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-webflux</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>        <dependency>            <groupId>com.squareup.okhttp3</groupId>            <artifactId>okhttp</artifactId>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-square-okhttp</artifactId>            <version>0.4.0-SNAPSHOT</version>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-loadbalancer</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <!-- 如果父工程不是springboot,就要用以下形式应用插件,能力生成失常的jar -->            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <configuration>                    <mainClass>com.bolingcavalry.ConsumerApplication</mainClass>                </configuration>                <executions>                    <execution>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>
  • 配置文件application.yml,还是常见的那几个配置:利用名、端口、eureka:
spring:  application:    name: consumer-okhttpserver:  port: 18081eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/
  • 启动类:
package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class OkhttpApplication {    public static void main(String[] args) {        SpringApplication.run(OkhttpApplication.class, args);    }}
  • 接下来是重要的配置类OkHttpClientConfig.java,用于实例化OkHttpClient.Builder对象并注册到spring环境:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationclass OkHttpClientConfig{    @Bean    @LoadBalanced    public OkHttpClient.Builder okHttpClientBuilder() {        return new OkHttpClient.Builder();    }}
  • 而后就能够应用这个Builder来创立OkHttpClient实例了,如下所示,可见入参request的url字段里应用了服务名<font color="blue">provider</font>,相当于OkHttpClient内如也能通过服务名获得具体的服务地址,至于是如何获取的,会在前面的文章详细分析,整段代码除了url应用服务名,并没有什么值得关注的中央了,一般的OkHttpClient应用而已:
package com.bolingcavalry.consumer.controller;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestControllerpublic class RemoteHello {    @Autowired    private OkHttpClient.Builder builder;    @GetMapping("/remote-str")    public String hello() throws IOException {        // 间接应用服务名        Request request = new Request.Builder().url("http://provider/hello-str").build();        // 近程拜访        Response response = builder.build().newCall(request).execute();        return "get remote response : " + response.body().string();    }}
  • 接下来看看单元测试代码,应用MockMvcRequestBuilders结构http申请,查看返回码和返回内容:
package com.bolingcavalry.consumer.controller;import com.bolingcavalry.client.Constants;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import static org.hamcrest.Matchers.containsString;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest {    @Autowired    private MockMvc mvc;    @Test    void hello() throws Exception {        String responseString = mvc.perform(MockMvcRequestBuilders.get("/remote-str").accept(MediaType.APPLICATION_JSON))                .andExpect(status().isOk())                .andExpect(content().string(containsString(Constants.HELLO_PREFIX)))                .andDo(print())                .andReturn()                .getResponse()                .getContentAsString();        log.info("response in junit test :\n" + responseString);    }}
  • 如果eureka和provider都运行起来了,那么此时能够间接运行单元测试类,顺利通过测试,如下图:

consumer-retrofit-okhttp,基于spring-cloud-square的okhttp能力

  • 接下来的两个利用都应用了当下热门的retrofit,再搭配Spring Cloud LoadBalance实现服务注册发现,当然了retrofit本身无奈实现网络申请解决,要依赖其余库,先看okhttp库的
  • 新建利用<font color="blue">consumer-retrofit-okhttp</font>,其pom.xml如下,要留神的必须依赖spring-cloud-square-retrofit和spring-cloud-square-okhttp,另外,为了:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud-square-tutorials</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>consumer-retrofit-okhttp</artifactId>    <dependencies>        <dependency>            <groupId>com.bolingcavalry</groupId>            <artifactId>client</artifactId>            <version>${project.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-square-retrofit</artifactId>            <version>0.4.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-square-okhttp</artifactId>            <version>0.4.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <!--skip deploy (this is just a test module) -->                <artifactId>maven-deploy-plugin</artifactId>                <configuration>                    <skip>true</skip>                </configuration>            </plugin>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>
  • 配置文件:
spring:  application:    name: consumer-retrofit-okhttpserver:  port: 18082eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/
  • 启动类:
package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class RetrofitOkhttpApplication {    public static void main(String[] args) {        SpringApplication.run(RetrofitOkhttpApplication.class, args);    }}
  • 配置类,和前一个利用的没啥区别,想想也是,底层可不都是okhttp么:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.square.retrofit.EnableRetrofitClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@EnableRetrofitClientsclass OkHttpClientConfig{    @Bean    @LoadBalanced    public OkHttpClient.Builder okHttpClientBuilder() {        return new OkHttpClient.Builder();    }}
  • 接下来,乏味的局部呈现了,先定义HelloService.java,外面的注解RetrofitClient指定了对应的服务名<font color="blue">provider</font>,在hello办法生,用GET注解指定了provider提供的web接口,而且hello办法的返回值Call<HelloResponse>,和provider服务中hello-obj的返回值HelloResponse也是对应的,还有就是hello的入参对应着provider服务中hello-obj的入参,很相熟吧,的确,和feign太像了:
package com.bolingcavalry.consumer.service;import com.bolingcavalry.client.HelloResponse;import org.springframework.cloud.square.retrofit.core.RetrofitClient;import retrofit2.Call;import retrofit2.http.GET;import retrofit2.http.Query;@RetrofitClient("provider")public interface HelloService {    @GET("/hello-obj")    Call<HelloResponse> hello(@Query("name") String name);}
  • 接下来是调用provider服务中hello-obj接口的代码RemoteHello.java,如下所示,神奇的一幕呈现了,方才咱们只写了HelloService接口,并没有写它的实现,然而通过Autowired注解却能 从spring环境拿到实例间接应用,在hello办法中,并没有见到近程调用的代码,而是执行helloService.hello,就能发动近程调用,拿到provider返回的后果:
package com.bolingcavalry.consumer.controller;import com.bolingcavalry.client.HelloResponse;import com.bolingcavalry.consumer.service.HelloService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestControllerpublic class RemoteHello {    @Autowired(required = false)    HelloService helloService;    @GetMapping("/remote-obj")    public HelloResponse hello(@RequestParam("name") String name) throws IOException {        return helloService.hello(name).execute().body();    }}
  • 看到这里,聪慧的您肯定会感觉欣宸就是个<font color="blue">没见过世面的乡巴佬</font>:定义HelloService 接口,无需开发实现类,这玩意在mybatis不就有了嘛,竟然敢说"神奇",我感觉您说得对,欣宸的确没见识,少见多怪的...
  • 单元测试类如下,因为返回的是json对象,因而能够用andExpect办法再配合MockMvcResultMatchers,对json进行查看:
package com.bolingcavalry.consumer.controller;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.hamcrest.Matchers.is;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest {    private MockMvc mvc;    @Autowired    private WebApplicationContext webApplicationContext;    @BeforeEach    public void setUp() {        // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1,        // 失去的字符串打印进去也是乱码,        // 上面的设置能够解决此问题        if (null==mvc) {            mvc = MockMvcBuilders                    .webAppContextSetup(webApplicationContext)                    .addFilter((request, response, chain) -> {                        response.setCharacterEncoding("UTF-8"); // this is crucial                        chain.doFilter(request, response);                    }, "/*")                    .build();        }    }    @Test    void hello() throws Exception {        // 申请参数是用户名,实时生成一个        String name = System.currentTimeMillis() + "程序员A";        // 申请        String responseString = mvc.perform(                MockMvcRequestBuilders                        .get("/remote-obj")                        .param("name", name)                        .accept(MediaType.APPLICATION_JSON)        )                .andExpect(status().isOk())                           // 验证状态                .andExpect(jsonPath("$.name", is(name)))    // 验证json中返回的字段是否含有name                .andDo(print())                .andReturn()                .getResponse()                .getContentAsString();        log.info("response in junit test :\n" + responseString);    }}
  • 执行单元测试,如下图,顺利通过:

consumer-retrofit-webflux,基于spring-cloud-square的retrofit + webflux

  • 最初退场的是consumer-retrofit-webflux,pom.xml如下,依赖库是spring-cloud-square-retrofit + spring-boot-starter-webflux的组合:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud-square-tutorials</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>consumer-retrofit-webflux</artifactId>    <dependencies>        <dependency>            <groupId>com.bolingcavalry</groupId>            <artifactId>client</artifactId>            <version>${project.version}</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-square-retrofit</artifactId>            <version>0.4.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-square-retrofit-webclient</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-webflux</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <!--skip deploy (this is just a test module) -->                <artifactId>maven-deploy-plugin</artifactId>                <configuration>                    <skip>true</skip>                </configuration>            </plugin>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>
  • 配置文件application.yml:
spring:  application:    name: consumer-retrofit-webfluxserver:  port: 18083eureka:  client:    serviceUrl:      defaultZone: http://localhost:8761/eureka/
  • 启动类RetrofitWebfluxApplication.java

    package com.bolingcavalry.consumer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class RetrofitWebfluxApplication {  public static void main(String[] args) {      SpringApplication.run(RetrofitWebfluxApplication.class, args);  }}
  • 配置类AppConfiguration.java,应用的注解是EnableRetrofitClients,实例化的Buider对象是WebClient.Builder,和后面的不一样,要分外留神:
package com.bolingcavalry.consumer;import okhttp3.OkHttpClient;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.square.retrofit.webclient.EnableRetrofitClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.reactive.function.client.WebClient;@Configuration@EnableRetrofitClientsclass AppConfiguration {    @Bean    @LoadBalanced    public WebClient.Builder builder() {        return WebClient.builder();    }}
  • 接下来是接口定义,留神hello办法的返回值是<font color="blue">Mono<HelloResponse></font>,这是weflux格调的返回值,代表异步的0个或一个元素:
package com.bolingcavalry.consumer.service;import com.bolingcavalry.client.HelloResponse;import org.springframework.cloud.square.retrofit.core.RetrofitClient;import reactor.core.publisher.Mono;import retrofit2.http.GET;import retrofit2.http.Query;@RetrofitClient("provider")public interface HelloService {    @GET("/hello-obj")    Mono<HelloResponse> hello(@Query("name") String name);}
  • 最初是单元测试类,和后面的没啥区别:
package com.bolingcavalry.consumer.controller;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.hamcrest.Matchers.is;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvc@Slf4jclass RemoteHelloTest {    private MockMvc mvc;    @Autowired    private WebApplicationContext webApplicationContext;    @BeforeEach    public void setUp() {        // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1,        // 失去的字符串打印进去也是乱码,        // 上面的设置能够解决此问题        if (null==mvc) {            mvc = MockMvcBuilders                    .webAppContextSetup(webApplicationContext)                    .addFilter((request, response, chain) -> {                        response.setCharacterEncoding("UTF-8"); // this is crucial                        chain.doFilter(request, response);                    }, "/*")                    .build();        }    }    @Test    void hello() throws Exception {        // 申请参数是用户名,实时生成一个        String name = System.currentTimeMillis() + "程序员B";        // 申请        String responseString = mvc.perform(                MockMvcRequestBuilders                        .get("/remote-obj")                        .param("name", name)                        .accept(MediaType.APPLICATION_JSON)        )                .andExpect(status().isOk())                           // 验证状态                .andExpect(jsonPath("$.name", is(name)))    // 验证json中返回的字段是否含有name                .andDo(print())                .andReturn()                .getResponse()                .getContentAsString();        log.info("response in junit test :\n" + responseString);    }}
  • 运行单元测试,如下图,顺利通过,并且红框中所示的中文也没有乱码:

  • 至此,spring-cloud-square的三种类型,咱们全副编码体验了一遍,聪慧的您当然不会只满足于应用它们,接下来文章,咱们就去深刻spring-cloud-square源码,钻研其实现的细节,欣宸原创,必不会辜负您的期待!

你不孤独,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢送关注公众号:程序员欣宸

微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游Java世界...
https://github.com/zq2599/blog_demos