随笔 - 435  文章 - 0  评论 - 111  阅读 - 62万 

新建SpringBoot项目在Start.spring.IO 选择版本,packageName,Dependency,生成脚手架,在POM.xml增加一个plugin, Properties增加一个项, 当然项目中要增加Dockerfile

复制代码
<!-- This plugin is used to create a docker image and publish the image to docker hub-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.4.13</version>
                <configuration>
                    <repository>${docker.image.prefix}/${project.artifactId}</repository>
                    <tag>${project.version}</tag>
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
                <executions>
                    <execution>
                        <id>default</id>
                        <phase>install</phase>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
复制代码
1
2
3
<properties>     
    <docker.image.prefix>ostock</docker.image.prefix>
</properties>

 

书中的DockerFile 其实可以简化

复制代码
#stage 1
#Start with a base image containing Java runtime
FROM openjdk:11-slim as build

# The application's jar file
ARG JAR_FILE

# Add the application's jar to the container
COPY ${JAR_FILE} app.jar


#Add volume pointing to /tmp
VOLUME /tmp

#execute the application
ENTRYPOINT ["java","-jar","/app.jar"]
复制代码

 

  

 

Chapter 4: install Lombok to use @Getter @Setter @ToString, build 出错时再build一次就好了.

       Docker-mave-plugin 在mac系统运行出错, 升级Mac版本到12.2以上就好了

Chapter 5:

    dockerhub里面有一个springcloud/configserver的镜像,大小只有200m, 我自己打包出来的快500m了. 但是它那个运行起来, 我进去docker的CLI发现没有JAVA命令的.

docker ps 查看入口点是 /cnb/lifecycle/laun

  configSever的作用是把众多的微服务的config,集中在一个地方管理  [URL]/[微服务名字]/[default/dev/prod]

  1.  build configserver docker image first, modify bootstrap.yml  line 6, delete "git" ,use local file system to store config file,  maven Build skip Test
    (其实可以把bootstrap.yml改名成application.yml, 不然ConfigurationServerApplicationTests 运行测试的时候就会找不到Git地址,所以原书上的代码是把Test 用@disalbed)
  2.  build licensing-service docker image
  3.  goto docker folder. use docker-compose up command,       I want find  a docker image size less then openjdk:11-slim(429M), but not found

  configserver 的性能监测,健康检查在 localhost:8071/actuator   native文件存储在 search-locations: classpath:/config 

  licensing-service 的dev环境的配置文件在localhost:8071/licensing-service/dev  对应的/src/main/resource/config目录下对应的[微服务的名字]-[Dev/prod].properties文件, 但是放在这个位置, 每次修改都要重新打包,太麻烦了. 可以改成 /usr/src/app/config/, 然后在docker-compose.yml里的增加volume映射

复制代码
spring:
  application:
    name: config-server
  profiles:
    active:
    - native
  cloud:
    config:
      server:
        native:
          search-locations: /usr/src/app/config/   #classpath:/config
复制代码
复制代码
 configserver:
    image: ostock/configserver:0.0.1-SNAPSHOT
    ports:
       - "8071:8071"
    environment:
      ENCRYPT_KEY: "fje83Ki8403Iod87dne7Yjsl3THueh48jfuO9j4U2hf64Lo"
    volumes:
        - ../configserver/src/main/resources/config:/usr/src/app/config/
    networks:
      backend:
        aliases:
          - "configserver"
复制代码

 如果要改用Git存储, (国内可以改用Gitee,速度快)示例上的yml文件,没有配置凭据,会出错,参考git配置SSH  spring cloud config server使用ssh方式连接

1
https://XXXXXXX/config.git: Authentication is required but no CredentialsProvider has been registered 

在 spring cloud config server 中使用 ssh 连接 git 仓库_HermitSun  但我测试时发现只有输入账号密码在yml里才能连接私有仓库,用SSH的方法不行,还没找到原因 

 

dockerfile-maven-plugin 打包时,需要在线,不然会报错(就算本地已经有openjdk:11的image)

configserver 变成单点了??? 假如configserver不能启动,其他微服务也不能启动了. 有data字段,假如连不上configserver时用???

Spring Cloud Config 实现配置中心,看这一篇就够了 

在licensing-service 看到这段代码,一开始我很震惊, 接口只声明了方法,没有实现都可以查询到....

@Repository
public interface LicenseRepository extends CrudRepository<License,String>  {
    public List<License> findByOrganizationId(String organizationId);
    public License findByOrganizationIdAndLicenseId(String organizationId,String licenseId);
    
}

后来才了解到这个是JPA的约定 继承Repository的接口在使用的时候,通过@Autowired会自动创建接口的实现类,不需要怎么去实现这个接口,这也是jpa最方便的地方

复制代码
1.findBy  findAllBy的区别
它们之间没有区别,它们将执行完全相同的查询,当从方法名称派生查询时,Spring Data会忽略All部分。唯一重要的一点是By关键字,其后面的任何内容都被视为字段名称

如 findXXXXXXXXXXXXXByName 实际上==》 findByName

2、JPA中支持的关键词
And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
NotNull --- 与 IsNotNull 等价;
Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);但是有一点需要注意的是,%需要我们自己来写
NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,
方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
复制代码

 ============================

Chapter 6:  客户端LoadBalancer, 是比单纯使用服务发现更坚固的方法. 当服务的客户端要使用服务时,先从本地缓存去找微服务(采用Round Robin 轮询调度算法. 就是以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器),找不到再去服务发现里找. 定时从服务发现里更新本地缓存(比如10分钟), 当访问某一个服务时出现Fail,则马上更新本地缓存. (这种客户端的负载均衡是强侵入,必须客户端用JAVA,Spring)

 居然代码跑不起来. 四个项目configServer, EurekaServer,licensing-service,organization-service.   其他三个项目都依赖configServer, 3个的bootrap.yml 都指定cloud config的地址  http://configserver:8071, 这个地址只能在docker里跑,但是我们还没有docker image文件, 首先在Eclipse里能跑起来才能打包成Image, 当eclipse运行configServer它默认是在http://localhost:8071, 我们又不想把bootrap.yml里面的内容改来改去,

有一个小办法. 可以在windows的host文件里把configServer 定义成127.0.0.1, 这样在宿主机和container里都能兼容

Mac 则是打开Finder,之后按下Shift+Command+G组合键,最后输入/etc/hosts回车即可找到hosts文件。

licensing-service,organization-service都依赖pgsql 和EurekaServer, pgsql 可以单独docker run起来, 但EurekaServer启动却出错了.

复制代码
022-02-06 00:11:19.223  INFO 13184 --- [           main] c.s.j.s.i.a.WebApplicationImpl           : Initiating Jersey application, version 'Jersey: 1.19.1 03/11/2016 02:08 PM'
2022-02-06 00:11:19.305  INFO 13184 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON encoding codec LegacyJacksonJson
2022-02-06 00:11:19.306  INFO 13184 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON decoding codec LegacyJacksonJson
2022-02-06 00:11:19.484 ERROR 13184 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Exception starting filter [servletContainer]

java.lang.ExceptionInInitializerError: null
    at com.thoughtworks.xstream.XStream.setupConverters(XStream.java:990) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.<init>(XStream.java:593) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.<init>(XStream.java:515) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.<init>(XStream.java:484) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.<init>(XStream.java:430) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.<init>(XStream.java:397) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.netflix.discovery.converters.XmlXStream.<init>(XmlXStream.java:51) ~[eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.converters.XmlXStream.<clinit>(XmlXStream.java:42) ~[eureka-client-1.9.13.jar:1.9.13]
复制代码

 网上查一下说某个jar包和springboot的版本不兼容的原因, xstream 改成新版, 又会有另一个错误 

 

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.19</version>
        </dependency>

Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/eureka/}  但最后能在8080端口启动

 

最后决定在POM.xml里把springBoot改成最新的2.6.3, 又出现了另一个错误

Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/context/properties/ConfigurationBeanFactoryMetadata

这个则是springBoot和springCloud使用的版本不一致导致的,在POM里改成<spring-cloud.version>2021.0.0</spring-cloud.version>

 


出现这个错误, No spring.config.import property has been defined, 要加一个依赖

 

 

 If you have set spring.cloud.config.server.bootstrap=true, you need to use a composite configuration. 新版本如果要用bootrap.yml, 需要另外加一个依赖

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

 升级了2.6.3后 licensing-service又有另一个错误, 把LicenseServiceApplication里面的localeResolver 的Bean注释掉

1
The bean 'localeResolver', defined in com.optimagrowth.license.LicenseServiceApplication, could not be registered. <br>A bean with that name has already been defined in class path resource

Every service registered with Eureka will have two components associated with it: the application ID and the instance ID.

Eureka注册的是bootrap.yml里面的spring.application.name作为程序ID, 实例ID则是一个随机数字  

在每个微服务的对应的configserver里的config文件里指定Eureka的地址端口

eureka.instance.preferIpAddress = true
eureka.client.registerWithEureka = true
eureka.client.fetchRegistry = true
eureka.client.serviceUrl.defaultZone = http://eurekaserver:8070/eureka/

licensing-service,organization-serivce, 如何启动多个实例在不同的端口呢? 在本机Docker-compose.yml里可以这样写,K8s就不用这样

复制代码
  organizationservice:
    image: ostock/organization-service:0.0.1-SNAPSHOT
    environment:
      PROFILE: "dev"
      CONFIGSERVER_URI: "http://configserver:8071"
      CONFIGSERVER_PORT:   "8071"
      DATABASESERVER_PORT: "5432"
      ENCRYPT_KEY:       "IMSYMMETRIC"
    depends_on:
      database:
        condition: service_healthy
      configserver:
        condition: service_started
    ports:
      - "8081:8081"
    networks:
      - backend
  organizationservice1:
    image: ostock/organization-service:0.0.1-SNAPSHOT
    environment:
      PROFILE: "dev"
      CONFIGSERVER_URI: "http://configserver:8071"
      CONFIGSERVER_PORT:   "8071"
      DATABASESERVER_PORT: "5432"
      ENCRYPT_KEY:       "IMSYMMETRIC"
    depends_on:
      database:
        condition: service_healthy
      configserver:
        condition: service_started
    ports:
      - "8082:8081"
    networks:
      - backend
复制代码

 

 

 但又引申出另一个问题, 我访问时我怎么知道是访问哪一个instance, 性能监控如何

 

 三种客户端的区别:  DiscoveryClient 有注册服务的功能,但它的写法没有使用客户端的负载均衡,而且需要程序员自己选择用哪一个实例.而且要写很多代码, 差评. 不要用它.

FeignClient基于REST的服务调用上提供更高级别的抽象, FeignClient简化了请求的编写,只要写一个接口的方法,不需要写实现. 且通过动态负载进行选择要使用哪个服务进行消费,而这一切都由Spring动态配置实现,我们不用关心这些,只管使用方法即可。RestTemplate还需要写上服务器IP这些信息. 

Using a Load Balancer–backed RestTemplate to call a service

(其实也不用,只要在LicenseServiceApplication里用@LoadBalanced注解RestTemplate ,RestTemplate 就能用http://organization-service 这样的写法,自动分配Round Robin 轮询调度)

1
2
3
4
5
6
7
8
9
10
11
public class LicenseServiceApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(LicenseServiceApplication.class, args);
    }
     
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

  

复制代码
public class OrganizationRestTemplateClient {
    @Autowired
    RestTemplate restTemplate;

    public Organization getOrganization(String organizationId){
        ResponseEntity<Organization> restExchange =
                restTemplate.exchange(
                        "http://organization-service/v1/organization/{organizationId}",
                        HttpMethod.GET,
                        null, Organization.class, organizationId);

        return restExchange.getBody();
    }
}
复制代码
@FeignClient("organization-service")
public interface OrganizationFeignClient {
    @RequestMapping(
            method= RequestMethod.GET,
            value="/v1/organization/{organizationId}",
            consumes="application/json")
    Organization getOrganization(@PathVariable("organizationId") String organizationId);
}

 如何Eureka 服务Down了, 访问licensing-service时,它会从Eureka来找Organization service就会出现这个错误

复制代码
{
"metadata": {
"status": "NOT_ACCEPTABLE"
},
"errors": [
{
"message": "No instances available for organization-service",
"code": null,
"detail": "No instances available for organization-service"
}
]
}
复制代码

Eureka Server在设计的时候就考虑了高可用设计,在Eureka服务治理设计中,所有节点既是服务的提供方,也是服务的消费方,服务注册中心也不例外。

Eureka Server的高可用实际上就是将自己做为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。
Eureka Server的同步遵循着一个非常简单的原则:只要有一条边将节点连接,就可以进行信息传播与同步。可以采用两两注册的方式实现集群中节点完全对等的效果,实现最高可用性集群,任何一台注册中心故障都不会影响服务的注册与发现。

 

 

 

Chapter 7:

把Licensing-service 升级到2.6.3 spring-boot, 然后把 <resilience4j.version>1.7.0</resilience4j.version> 改成1.7.0, 默认的设置,每次都会fallback, 先把注解去掉,一个个再加上看看是哪一个问题

 

复制代码
//    @CircuitBreaker(name = "licenseService", fallbackMethod = "buildFallbackLicenseList")
//    @RateLimiter(name = "licenseService", fallbackMethod = "buildFallbackLicenseList")
//    @Retry(name = "retryLicenseService", fallbackMethod = "buildFallbackLicenseList")
//    @Bulkhead(name = "bulkheadLicenseService", type= Type.THREADPOOL, fallbackMethod = "buildFallbackLicenseList")
    public List<License> getLicensesByOrganization(String organizationId) throws TimeoutException {
        logger.debug("getLicensesByOrganization Correlation id: {}",
                UserContextHolder.getContext().getCorrelationId());
        randomlyRunLong();
        return licenseRepository.findByOrganizationId(organizationId);
    }
复制代码

 

posted on   Gu  阅读(246)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
点击右上角即可分享
微信分享提示