<Spring MicroService In Action> 读书笔记


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

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

Chapter 5:

  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
  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文件,没有配置凭据,会出错,参考  spring cloud config server使用ssh方式连接

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  {
    public List 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 userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection 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里都能兼容

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.(XStream.java:593) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.(XStream.java:515) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.(XStream.java:484) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.(XStream.java:430) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.thoughtworks.xstream.XStream.(XStream.java:397) ~[xstream-1.4.11.1.jar:1.4.11.1]
    at com.netflix.discovery.converters.XmlXStream.(XmlXStream.java:51) ~[eureka-client-1.9.13.jar:1.9.13]
    at com.netflix.discovery.converters.XmlXStream.(XmlXStream.java:42) ~[eureka-client-1.9.13.jar:1.9.13]

 网上查一下说某个jar包和springboot的版本不兼容的原因, 然后在POM.xml里把springBoot改成最新的2.6.3, 又出现了另一个错误

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

这个则是springBoot和springCloud使用的版本不一致导致的,在POM里改成2021.0.0

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


    org.springframework.cloud
    spring-cloud-starter-bootstrap

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

The bean 'localeResolver', defined in com.optimagrowth.license.LicenseServiceApplication, could not be registered. 
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 轮询调度)

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 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"
}
]
}